--|                                                      |--
--| ListBot v 1.53                                       |--
--|     Author: Hellkeepa (hellkeeper_1@hotmail.com)     |--
--|                                                      |--
--| CopyLeft, but please let me know if you change       |--
--| anything. Always remember to credit original author: |--
--| No-one appreciates having their work stolen. ;-)     |--
--|                                                      |--
--| A thousand thanks to the following people for        |--
--| helping me with development and testing:             |--
--|    EmOuBi (feature requests & bug-testing)           |--
--|    Frog-sama (bug-testing & -fixing)                 |--
--|    kme (feature requests & bug-testing)              |--
--|    Dr.Lee (feat. req, de-bugging and some coding)    |--
--|    ZeroSleep (feat. req, bug-testing & -fixing)      |--
--|    Hurbold (bug-testing & -fixing)                   |--
--|    Miraja (feat. reg, de-bugging and some coding)    |--
--|                                                      |--

--[[ New features?
-- +rlsfind string --> To search for a release
-- Sub-types.
-- Comment/review handling & table.
-- Rename type.
-- Change detail command, "!cdata <field> <new val>"
-- Per-user language selection
-- Check for existing title.
-- "Top10 in users' list" under "comments"
-- Manual and automatic (toggable) notifying of new releases.
]]--

if true then -- To hide the bloody changelog.
--[[ Changelog:
-- 1.53 Fixed various bugs and typos that would
		prevent the script from working correctly,
		and added a "OnUnLoad()" event handler.

-- 1.52	Moved the event handlers into CFL syntax,
		and fixed a bug in "GetPM()" decleration.

-- 1.51	Cleaned up error messages a bit, moved the
		common functions into a file of their own,
		added checks for sending right-click commands,
		put in language selection for the RC menu,
		added own class for "add" functions

-- 1.50	Added submission date, tweaked layout a bit,
		fixed a bug with the Right-Click menu, rewrote
		the code to support external language files,
		added new "list by type" menu (thx Miraja),
		and created "list files by user" functions.
		** New functions:
		ShowList(); AddBot(); FormatTF(); ListUser();
		UserFiles(); SetLan();

-- 1.40	Added support for web-based frontend, added
		variable to set how many news to list, and
		added a simple "user's top-10" system. Also
		fixed the right-click menu a bit, and some
		minor tweaks overall in the script.

-- 1.32	Changed the layout of the remaining "!list"
		options for files, thanks to Dr.Lee again. :-)

-- 1.31	Fixed blocking of PMs, and cleaned up the code
		of the PM event handler a bit.

-- 1.30	Removed newline from start-up message, fixed the
		"Warning" message popping up on all PM-commands,
		moved the "!ctype" help into admin-only part, and
		fixed item-counting when using "!ctype".
		New feature: Right-click menu, thanks to Dr.Lee!
		Also added toggable news PM to connecting users,
		and command to configure the alerts & menu.

-- 1.24	Changed sorting for "!list new" to "ASC" &
		added a missing space in the SQL query, added
		a "+" to the bot's description, cleaned up help
		message a bit, added custom messages on "!list *"
		when no results found, slight change to the
		layout of the "!list new" report, fixed bug with
		using "string" type file type in "!fadd", same
		problem with "!fdel" which is fixed as well, and
		fixed bug with wrong variable being used in "!dtype".
		Lastly I removed bogus "failed" message in "!ctype",
		and added a "unknown command" for PMs.

-- 1.23	Fixed bug in SQL-query for "!fdel", moved "!ctype"
		to admin commands, fixed missing "s" in table
		name for "!list new", and corrected error-
		message in "!fdel" to say "ID" instead of "Type".
		Also made it sort types on "!list types", and
		added some beautification code to paths.

-- 1.22	Changed layout of "!list new" report, thanks
		to Dr.Dre for making it for me :-), changed
		the "type" in listing from id to text
		and added a fail-safe check at the same time.
		Also added "!ctype" command, to change the
		type of a file.
		Changed "id" in "!fdel" file-deletion query
		to "type", and fixed wrong placement of the
		starting paranthesis in "Check_Commands()"
		for "!list range".

-- 1.21	Fixed a bug with unset "tid" in "!fdel",
		added a check for unset "tt" in "!list all",
		and added a confirmation message to "!fadd".
		Also fixed a bug in the syntax of some calls
		to "MsgOps()", and added an error-catching
		routine for "!fdel".
		Last, but not least, I added missing help-message
		for "!list range", and finally made the script
		delete all files of type in "!dtype".

-- 1.20	Added "list all" & "range <min> <max>" functions,
		and fixed access-check in the help message to
		use the "set_cls_admin" variable when checking
		for access to admin commands. Also added
		support for commands to the Bot via PM, and
		some minor tweaks to the event handler code:
		Removed some unused and unneccesary variables,
		and shelled out command parsing/execution to
		a function of it's own: "Check_Commands()"

-- 1.11	Fixed typo in one SQL query, from "addr" to
		"adder". Also fixed DB update script a bit.

-- 1.10	Added item count for types, "added by" field
		for files list, and support for string in
		"type" field when adding files.

-- 1.02	Fixed problem with "ttype" var being "nil".

-- 1.01	Fixed problem with bot e-mail variable,
		fixed wrong index used in "ParseData()",
		added escaping of slashes for "!fadd",
		and added better formatting for lists.

-- 1.00	Cleaned up code, and released it as "stable".

-- 0.96	Reverted optimalization changes in "ParseData()".

-- 0.95	Fixed "AddFile()" RegExp to support "+fadd", and added
		test to check if data has been submitted correctly.
		Also fixed test in "StrFix()" to check for "nil".

-- 0.94	Added support for +set_cmd_help, replaced RegExps
		for "!fadd" with custom function, and added reporting
		PMs to add and delete commands w/on-off variable.
		Added support for "+" to all commands, and fixed
		"IsClass()" to return true on class as well.
		ALso added two new control variables, to customize
		who gets access to the bot.

-- 0.93	Added missing "then" in "Main()", and added
		support for "+" commands for general users.
		Also changed help-message to have variable
		prefix, "!" for OPs or "+" for users.

-- 0.92	Added a missing underscore (_) at line 647,
		added a missing newline to the help-message,
		and added a missing "res, " at line 542.
		Also added the "use_existing_bot" variable,
		re-arranged the order of ID and type in "!list types",
		and rewrote the test for legal chars in the
		"StrFix()" function.

-- 0.91	Removed debugging code, and fixed the if()
		statement for the determination of file-type
		passed. Also cleaned up the "FixStr()" RegExps.

-- 0.90	Added parsing of user-called help command,
		added check for ID-number check on file-adding,
		and minor RegExp re-write for "!fadd" and "!list".

-- 0.85	Added checks for NULL-fields from database
		in "ListNew()" and "ListFiles()" functions,
		also added type-casting to number for ID-checks
		in "AddFile()".

-- 0.84	Fixed a few errors with variable names, and
		renamed "VH:SQLFetchRow()" to the correct
		"VH:SQLFetch()". Changed RegExp in "!fadd",
		removed double-slashes. And finally added
		a forgotten instance of "SF()".

-- 0.83	Fixed a few SQL errors, added a few checks
		for correct values, and a couple of typos.
		Thanks to Dr.Lee for pointing these out. :-)

-- 0.82	Fixed several syntax errors and typos, added
 		better handling of some errors, and changed to
		correct database handling (uses FetchRow now to
		actually retrieve the data) for all queries.

-- 0.81	Added file-type check for "!list" command, and
		rewrote the RegExp for "!fadd" to take the 3 neccesary
		parametres only.

-- 0.8	Added double-escaping to "denylist" variable, and
		removed a forgotten block of code.

-- 0.71	Fixed broken newline, which left string open.

-- 0.7	Added new functions: DelFile(), DelType() & ListNew().
		Updated help message.
		Changed ListFiles() to work with both ID and name of type.
		Added possibility for use of quotes in !fadd.
		Shortened down some very long lines.
		Changed the class of the bot to 10.
		Fixed SQL for creating "files_type" table.
]]--
end;

if true then -- To hide the global variables.
-- Select the language you want to use with it's two-char language code (ex: "en", "no", or "fr").
set_bot_lang = "en";

-- Give users of this class, and above, rights to add and delete files & types.
set_cls_admin = 4;

-- Give users of this class ability to use the "add" commands.
set_cls_add = 2;

-- Give users of this class, and above, rights to view files and types.
set_cls_user = 0; -- Controls the use of "set_cls_top" commands as well.

-- Give users of this class, and above, rights to have their own Top-10 lists.
set_cls_top = 1; -- Recommended to be at least 1 to protect the lists.

-- Command for user-help, change it if you want to.
set_cmd_help = "help_files";

-- Use existing bot? "true" is yes, and "false" is no.
use_existing_bot = false;

-- The name for the bot, set to existing bot if above is "true".
set_bot_name = "ListBot"

-- Replace with your e-mail address.
set_bot_email = "hellkeeper_1 (at) hotmail (dot) com";

-- Change this too, if you want; Bot-description.
set_bot_desc = "Write +"..set_cmd_help.." in main for commands.";

-- Name for table. Can be changed, but no need.
set_db_table = "files_list";

-- Table for file-types: "Game", "Music", etc. (Same as above)
set_db_types = "files_type";

-- Table for Top-10 lists, same as for "set_db_table".
set_db_top = "files_top";

-- Table for variables & language. 
-- set_db_var = "files_var";

-- Domain & base-path for web-hosting.
set_wh_path = "aaremma.pointclark.net/vh"

-- Controls how many files to list in the "news" section.
set_no_news = 10;

-- Determine wether or not URL (web-path) should be used in lists.
set_wh_use = false;

-- Determine wether or not all admins should be alerted on add/del.
set_op_alert = true;

-- Set this to "true" to PM the "10 newest files" to connecting users, "false" disables.
set_use_news = false;

-- Set this to "true" to send the right-click menu to connecting users, "false" disables.
set_use_menu = true;

-- Set this to "true" to enable the use of personal Top-10 lists.
set_use_top10 = true;

-- No need to change this
set_str_lim = "----------------------------------------------------------------------------------------------------------------------------------------------";


--|                                                      |--
--| Please do not edit anything below, unless you        |--
--| _know_ what you're doing: Very good chance of        |--
--| rendering the script unusable otherwise.             |--
--|                                                      |--
end;

-- Include the correct language file.
--require("listbot_"..set_bot_lang..".lua.inc");

-- Inlcude the functions file.
--require("functions.lua.inc");

-- Main function, is executed when starting HUB / loading script.
function Main()
	-- Define local variables, for later use.
	local db_field, res, err, count;

	-- Check if script should add a new bot.
	if (not use_existing_bot or use_existing_bot == 0) then
		-- Add new bot.
		AddBot(set_bot_name, set_bot_desc, set_bot_email);
	else
		set_bot_name = GetConfig("hub_security");
	end;

	-- Create table if it doesn't exist.
	db_fields = " (id MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT, type TINYINT(1) NOT NULL, "..
		"title VARCHAR(100) NOT NULL, file VARCHAR(255) NOT NULL, path VARCHAR(255), "..
		"comment VARCHAR(255), adder VARCHAR(60) NOT NULL DEFAULT \"N/A\", dateadd DATETIME, PRIMARY KEY (id))";
	sql = "CREATE TABLE IF NOT EXISTS "..set_db_table..db_fields;
	res, err = SQ(sql);

	-- Check if creation was successful.
	if (not res) then
		-- Table creation failed, send error message.
		message = "\r\n*** "..LB_Lan["err"].."\r\n*** "..LB_Lan["err_crt_tbl"]..set_db_table.."."..
				"\r\n*** "..LB_Lan["err_msg"]..err.."\r\n*** "..LB_Lan["err_sql"]..sql.."\r\n";
		MsgMain(set_bot_name, message, 5);
		return false;
	end;

	-- Create types table.
	db_fields = " (id TINYINT(1) NOT NULL AUTO_INCREMENT, type varchar (30) NOT NULL, count int(4) NOT NULL, PRIMARY KEY (id))";
	sql = "CREATE TABLE IF NOT EXISTS "..set_db_types..db_fields;
	res, err = SQ(sql);

	-- Check if creation was successful.
	if (not res) then
		-- Table creation failed, send error message.
		message = "\r\n*** "..LB_Lan["err"].."\r\n*** "..LB_Lan["err_crt_tbl"]..set_db_types.."."..
				"\r\n*** "..LB_Lan["err_msg"]..err.."\r\n*** "..LB_Lan["err_sql"]..sql.."\r\n";
		MsgMain(set_bot_name, message, 5);
		return false;
	end;

	-- Check if types table is empty.
	sql = "SELECT * FROM "..set_db_types;
	res, count = SQ(sql);
	if (count == 0) then
		-- Table empty, add pre-set types.
		values = "('Movies'),('DVD'),('Serials'),('XXX'),('Games'),('Anime'),('Manga'),('Programs'),('Music'),('Videoclips'),('Documents'),('Foto')";
		sql = "INSERT INTO "..set_db_types.." (type) VALUES "..values
		res, err = SQ(sql);

		-- Check if insertion was successful.
		if (not res) then
			-- Data insertion failed, send error message.
			message = "\r\n*** "..LB_Lan["err"].."\r\n*** "..LB_Lan["err_sed_tbl"]..set_db_types.."."..
					"\r\n*** "..LB_Lan["err_msg"]..err.."\r\n*** "..LB_Lan["err_sql"]..sql.."\r\n";
			MsgMain(set_bot_name, message, 5);
			return false;
		end;
	end;

	-- Create Top-10 userlists table.
	db_fields = " (nick VARCHAR(60) NOT NULL, f1 MEDIUMINT(8) UNSIGNED, r1 TINYINT(1) NOT NULL, "..
			"f2 MEDIUMINT(8) UNSIGNED, r2 TINYINT(1) NOT NULL, f3 MEDIUMINT(8) UNSIGNED, r3 TINYINT(1) NOT NULL, "..
			"f4 MEDIUMINT(8) UNSIGNED, r4 TINYINT(1) NOT NULL, f5 MEDIUMINT(8) UNSIGNED, r5 TINYINT(1) NOT NULL, "..
			"f6 MEDIUMINT(8) UNSIGNED, r6 TINYINT(1) NOT NULL, f7 MEDIUMINT(8) UNSIGNED, r7 TINYINT(1) NOT NULL, "..
			"f8 MEDIUMINT(8) UNSIGNED, r8 TINYINT(1) NOT NULL, f9 MEDIUMINT(8) UNSIGNED, r9 TINYINT(1) NOT NULL, "..
			"f0 MEDIUMINT(8) UNSIGNED, r0 TINYINT(1) NOT NULL, PRIMARY KEY (nick))";
	sql = "CREATE TABLE IF NOT EXISTS "..set_db_top..db_fields;
	res, err = SQ(sql);

	-- Check if creation was successful.
	if (not res) then
		-- Table creation failed, send error message.
		message = "\r\n*** "..LB_Lan["err"].."\r\n*** "..LB_Lan["err_crt_tbl"]..set_db_top.."."..
				"\r\n*** "..LB_Lan["err_msg"]..err.."\r\n*** "..LB_Lan["err_sql"]..sql.."\r\n";
		MsgMain(set_bot_name, message, 5);
		return false;
	end;

	-- Table created, send "ok" message.
	MsgMain(set_bot_name, LB_Lan["running"]..".", 5);
	return 1;
end;

-- Unloader function, to clean up some stuff.
function hub.Unload()
	-- Check if script has added a new bot.
	if (not use_existing_bot or use_existing_bot == 0) then
		-- It has, now remove the bot.
		RemoveBot(set_bot_name);
	end;

	return 1;
end;

-- Is executed when a user connects to the HUB.
function hub.OnUserLogin(nick)
	-- Check if "Connecting News" is turned on.
	if (set_use_news) then
		-- On, send news-list to user.
		ListNew(nick);
	end;

	-- Check if menu is to be sent.
	if (set_use_menu) then
		-- Set the title to be used for the menu-entry.
		local title = "ListBot";

		-- Check what prefix to use.
		if (IsClass(nick, 3)) then
			prefix = "!";
		else
			prefix = "+";
		end;

		-- Check if user has access to user functions
		if (IsClass(nick, set_cls_user)) then
			-- Create and send menu.
			MenuOpt(nick, 1, 3, title, LB_Lan["rm1_1"].." "..set_no_news.." "..LB_Lan["rm1_2"], prefix.."list new");
			MenuOpt(nick, 1, 3, title, LB_Lan["rm2"], prefix.."list all");
			MenuOpt(nick, 1, 3, title, LB_Lan["rm3"], prefix.."list range %[line:"..LB_Lan["rm3_p1"].."] %[line:"..LB_Lan["rm3_p2"].."]");
			MenuOpt(nick, 1, 3, title, LB_Lan["rm4"], prefix.."list %[line:"..LB_Lan["rm4_p1"].."]");
			MenuDiv(nick, 3);

			-- Check if user has access to "!fadd" function.
			if (IsClass(nic, set_cls_add)) then
				MenuOpt(nick, 1, 3, title, LB_Lan["rm5"], prefix.."fadd %[line:"..LB_Lan["rm5_p1"].."] %[line:"..LB_Lan["rm5_p2"].."] %[line:"..LB_Lan["rm5_p3"].."] %[line:"..LB_Lan["rm5_p4"].."] %[line:"..LB_Lan["rm5_p5"].."]");
			end;

			-- Check if user has access to admin functions.
			if (IsClass(nick, set_cls_admin)) then
				MenuOpt(nick, 1, 3, title, LB_Lan["rm6"], prefix.."ctype %[line:"..LB_Lan["rm6_p1"].."] %[line:"..LB_Lan["rm6_p2"].."]");
				MenuOpt(nick, 1, 3, title, LB_Lan["rm7"], prefix.."fdel %[line:"..LB_Lan["rm7_p1"].."]");
				MenuDiv(nick, 3);
			end;

			MenuOpt(nick, 1, 3, title, LB_Lan["rm8"], prefix.."list types");

			-- Check if user has access to "!fadd" function.
			if (IsClass(nic, set_cls_add)) then
				MenuOpt(nick, 1, 3, title, LB_Lan["rm9"], prefix.."ntype %[line:"..LB_Lan["rm9_p1"].."]");
			end;

			-- Check if user has access to admin functions.
			if (IsClass(nick, set_cls_admin)) then
				MenuOpt(nick, 1, 3, title, LB_Lan["rm10"], prefix.."dtype %[line:"..LB_Lan["rm10_p1"].."]");
			end;

			MenuDiv(nick, 3);
			MenuOpt(nick, 1, 3, title, LB_Lan["rm11"], prefix.."ltop %[line:"..LB_Lan["rm11_p1"].."]");

			-- Check if user has access to own Top-10 list.
			if (IsClass(user, set_cls_top)) then
				MenuOpt(nick, 1, 3, title, LB_Lan["rm12"], prefix.."atop %[line:"..LB_Lan["rm12_p1"].."] %[line:"..LB_Lan["rm12_p1"].."]");
				MenuOpt(nick, 1, 3, title, LB_Lan["rm13"], prefix.."rtop %[line:"..LB_Lan["rm12_p1"].."] %[line:"..LB_Lan["rm13_p1"].."]");
				MenuOpt(nick, 1, 3, title, LB_Lan["rm14"], prefix.."dtop %[line:"..LB_Lan["rm12_p1"].."]");
			end;

			MenuDiv(nick, 3);
			MenuOpt(nick, 1, 3, title, LB_Lan["rm15"], prefix.."help_files");

			-- Check if user is HUB admin, or higher.
			if (IsClass(nick, 5)) then
				-- User is admin, create and send configuration menu.
				MenuDiv(nick, 3);
				MenuOpt(nick, 1, 3, title, LB_Lan["rm16"], prefix.."lbs alert");
				MenuOpt(nick, 1, 3, title, LB_Lan["rm17"], prefix.."lbs news");
				MenuOpt(nick, 1, 3, title, LB_Lan["rm18"], prefix.."lbs newsno %[line:"..LB_Lan["rm18_p1"].."]");
				MenuOpt(nick, 1, 3, title, LB_Lan["rm19"], prefix.."lbs menu");
				MenuOpt(nick, 1, 3, title, LB_Lan["rm20"], prefix.."lbs top10");
				MenuOpt(nick, 1, 3, title, LB_Lan["rm21"], prefix.."lbs usetop %[line:"..LB_Lan["rm21_p1"].."]");
				MenuOpt(nick, 1, 3, title, LB_Lan["rm22"], prefix.."lbs webuse");
				MenuOpt(nick, 1, 3, title, LB_Lan["rm23"], prefix.."lbs webpath %[line:"..LB_Lan["rm23_p1"].."]");
				MenuOpt(nick, 1, 3, title, LB_Lan["rm24"], prefix.."lbs lang %[line:"..LB_Lan["rm24_p1"].."]");
				MenuOpt(nick, 1, 3, title, LB_Lan["rm25"], prefix.."lbs useadd %[line:"..LB_Lan["rm25_p1"].."]");
			end;
		end;
	end;

	-- Continue parsing.
	return 1;
end;

-- Is executed when somone triggers a "!" command.
function hub.OnOpCmd(nick, data)
	-- Check for, and execute, commands.
	ret = Check_Commands(nick, data);

	-- Check if command succeeded.
	if (ret == 1) then
		-- Command succeeded, and no further parsing needed.
		return 0;
	end;

	-- Continue parsing.
	return 1;
end;

-- Executed on all user commands ("+" commands).
function hub.OnUserCmd(nick, data)
	-- Check for, and execute, commands.
	ret = Check_Commands(nick, data);

	-- Check if command succeeded.
	if (ret == 1) then
		-- Command succeeded, and no further parsing needed.
		return 0;
	end;

	-- Continue parsing.
	return 1;
end;

-- Grab all PMs sent on HUB.
function hub.GetPm(nick, data, to)
	-- Check if PM is sent to bot.
	if (to == set_bot_name) then
		-- PM sent to bot. Check for, and execute, commands.
		ret = Check_Commands(nick, data);

		-- Check if command succeeded, and if extra bot was added.
		if (ret == 0 and (not use_existing_bot or use_existing_bot == 0)) then
			-- Send "command not found" to user, and abort.
			MsgPm(set_bot_name, nick, LB_Lan["wrn"]..LB_Lan["wrn_not_fnd"]..".");
		elseif (ret == 1 or (ret == 2 and use_existing_bot and use_existing_bot ~= 0)) then
			-- Command succeeded, and no further parsing needed.
		else
			-- No commands passed, show warning.
			MsgPm(set_bot_name, nick, LB_Lan["wrn"]..LB_Lan["wrn_not_acp"]..".");
		end;

		-- Was to bot, so stop parsing to save resources.
		return 0;
	end;

	-- Continue parsing.
	return 1;
end;

-- Checks for commands.
function Check_Commands(nick, data)
	-- Define local variables, for later use.
	local fi, ti;

	-- Check if user is HUB admin, or higher.
	if (IsClass(nick, 5)) then
		-- User is of admin-class, or higher, check what command was used.
		if (string.find(data, "^[%+!]lbs")) then
			-- Configure command called, parse data and call function.
			_, _, data = string.find(data, "[%+!]lbs%s([%w ]+)");
			SetConfig(nick, data);
			return 1;
		end;
	end;

	-- Check if user is of "admin" class.
	if (IsClass(nick, set_cls_admin)) then
		-- User has access to "admin" commands, check what command was used.
		if (string.find(data, "^[%+!]fdel%s")) then
			-- Requested file-deletion, sort variables and call function.
			_, _, data = string.find(data, "[%+!]fdel%s(%d+)");
			DelFile(nick, data);
			return 1;
		end;

		if (string.find(data, "^[%+!]ntype%s")) then
			-- Requested new type, sort variables and call function.
			_, _, data = string.find(data, "[%+!]ntype%s(%w+)");
			AddType(nick, data);
			return 1;
		end;

		if (string.find(data, "^[%+!]ctype%s")) then
			-- Requsted type-change, get file ID + type ID and call function.
			_, _, fi, ti = string.find(data, "[%+!]ctype%s(%d+)%s(%d+)");
			ChangeType(nick, fi, ti);
			return 1;
		end;

		if (string.find(data, "^[%+!]dtype%s")) then
			-- Requested deletion of type, sort variables and call function.
			_, _, data = string.find(data, "[%+!]dtype%s([%w%d]+)");
			DelType(nick, data);
			return 1;
		end;
	end;

	-- Check if user has access to "add" commands.
	if (IsClass(nick, set_cls_admin) or IsClass (nick, set_cls_add)) then
		if (string.find(data, "^[%+!]fadd%s")) then
			-- Requested file-add, call function.
			AddFile(nick, data);
			return 1;
		end;
	end;

	-- Check if user is of "user" class.
	if (IsClass(nick, set_cls_user)) then
		-- User has access to "user" commands, check what command was used.
		if (string.find(data, "^[%+!]"..set_cmd_help)) then
			-- User asked for help, get prefix and send help-message for bot.
			_, _, data = string.find(data, "([%+!])"..set_cmd_help);
			MsgHelp(nick, data);
			return 1;
		end;

		if (string.find(data, "^[%+!]list types")) then
			-- User requested type list, call function.
			ListTypes(nick);
			return 1;
		end;

		if (string.find(data, "^[%+!]list new")) then
			-- User requested newest added list, call function.
			ListNew(nick);
			return 1;
		end;

		if (string.find(data, "^[%+!]list range")) then
			-- User requested ranged list, get parametres and call function.
			_, _, data = string.find(data, "[%+!]list%s(range%s%d+%s%d+)");
			ListFiles(nick, data);
			return 1;
		end;

		if (string.find(data, "^[%+!]list%s")) then
			-- User requested list, get type and call list-function.
			_, _, data = string.find(data, "[%+!]list%s([%d%w ]+)");
			ListFiles(nick, data);
			return 1;
		end;

		if (string.find(data, "^[%+!]ulist")) then
			-- User requested list of users, call function.
			ListUsers(nick);
			return 1;
		end;

		if (string.find(data, "^[%+!]ufiles%")) then
			-- User requested list over users files, get nick and call function.
			_, _, data = string.find(data, "ulist%s(.*)");
			UserFiles(nick, data);
			return 1;
		end;

		if (data == "+help" or data == "!help") then
			-- User asked for help, get prefix and add help-message for bot.
			_, _, data = string.find(data, "([%+!])help");
			MsgHelp(nick, data);
			return 2;
		end;

		-- Check if Top-10 list is activated
		if (set_use_top10) then
			-- Activated, check what command was used.
			if (string.find(data, "^[%+!]ltop")) then
				-- Asking for Top-10 list, call function.
				ListTop(nick, data);
				return 1;
			end;

			if (string.find(data, "^[%+!]atop%s")) then
				-- Adding file to Top-10 list, call function.
				AddTop(nick, data);
				return 1;
			end;

			if (string.find(data, "^[%+!]dtop%s")) then
				-- Removing file from Top-10 list, parse data and call function.
				_, _, data = string.find(data, "dtop%s(%d+)");
				DelTop(nick, data);
				return 1;
			end;

			if (string.find(data, "^[%+!]rtop%s")) then
				-- Changing score of file in Top-10 list, call function.
				RankTop(nick, data);
				return 1;
			end;
		end;
	end;
end;

-- Toggle configuration variables.
function SetConfig(user, rule)
	-- Define local variables, for later use.
	local message, val, temp;

	-- Split "rule" into "name" and "value", if last one is set.
	_, _, temp, val = string.find(rule, "(%w+)%s?([%w%d]*)");

	-- Check if rule name was found.
	if (temp and temp ~= "") then
		-- Found, set rule name correctly.
		rule = temp;
	end;

	-- Check what rule to toggle.
	if (rule == "alert") then
		-- OP alert found, check status.
		if (set_op_alert == true) then
			-- Op alert active, turn off and set correct message.
			set_op_alert = false;
			message = LB_Lan["lbs_op_off"];
		else
			-- Op alert not active, turn on and set correct message.
			set_op_alert = true;
			message = LB_Lan["lbs_op_on"];
		end;
	elseif (rule == "news") then
		-- Connecting news found, check status.
		if (set_use_news == true) then
			-- Connecting news active, turn off and set correct message.
			set_use_news = false;
			message = LB_Lan["lbs_new_off"];
		else
			-- Connecting news not active, turn on and set correct message.
			set_use_news = true;
			message = LB_Lan["lbs_new_on"];
		end;
	elseif (rule == "newsno") then
		-- News number rule found, check if "value" has been set.
		if (not val or not tonumber(val)) then
			-- Not set, set warning and abort.
			message = "*** "..LB_Lan["wrn"]..LB_Lan["lbs_val_err"]..".";
		else 
			-- Update the global variable, and set correct mesasge.
			set_no_news = val;
			message = LB_Lan["lbs_new_set"].."'"..val.."'";
		end;
	elseif (rule == "menu") then
		-- Right-click menu found, check status.
		if (set_use_menu == true) then
			-- Right-click menu active, turn off and set correct message.
			set_use_menu = false;
			message = LB_Lan["lbs_mnu_off"];
		else
			-- Right-click menu not active, turn on and set correct message.
			set_use_menu = true;
			message = LB_Lan["lbs_mnu_on"];
		end;
	elseif (rule == "top10") then
		-- Top-10 lists found, check status.
		if (set_use_top10 == true) then
			-- Top-10 lists active, turn off and set correct message.
			set_use_top10 = false;
			message = LB_Lan["lbs_t10_off"];
		else
			-- Top-10 lists not active, turn on and set correct message.
			set_use_top10 = true;
			message = LB_Lan["lbs_t10_on"];
		end;
	elseif (rule == "usetop") then
		-- Top-10 user class found, check if "value" has been set.
		if (not val or not tonumber(val)) then
			-- Not set, set warning and abort.
			message = "*** "..LB_Lan["wrn"]..LB_Lan["lbs_val_err"]..".";
		else 
			-- Update the global variable, and set correct mesasge.
			set_cls_top = val;
			message = LB_Lan["lbs_t10_set"].."'"..val.."'";
		end;
	elseif (rule == "webuse") then
		-- Web-use toggle found, check status.
		if (set_wh_use == true) then
			-- Web-path use active, turn off and set correct message.
			set_wh_use = false;
			message = LB_Lan["lbs_web_off"];
		else
			-- Web-path use not active, turn on and set correct message.
			set_wh_use = true;
			message = LB_Lan["lbs_web_on"];
		end;
	elseif (rule == "webpath") then
		-- Web-path rule is found, check if "value" has been set.
		if (not val or val == "") then
			-- Not set, set warning and abort.
			message = "*** "..LB_Lan["wrn"]..LB_Lan["lbs_val_err"]..".";
		else
			-- Check if the string starts with "HTTP://".
			if (string.sub(val, 1, 7) == "http://") then
				-- Remove "http://" from the beginning, and set the variable.
				val = string.sub(val, 8);
			end;

			-- Check if the string ends with "/".
			if (string.sub(val, -1) == "/") then
				-- Remove last char.
				val = string.sub(val, 1, -2);
			end;

			-- Update the global variable, and set correct message.
			set_wh_path = val;
			message = LB_Lan["lbs_web_set"].."'"..val.."'";
		end;
	elseif (rule == "lang") then
		-- Language rule is found, check if "value" has been set.
		if (not val or val == "") then
			-- Not set, set warning and abort.
			message = "*** "..LB_Lan["wrn"]..LB_Lan["lbs_val_err"]..".";
		else
			-- Do the language change, and test if successful.
			if (SetLan(val)) then
				-- Successful, show message.
				message = LB_Lan["lbs_web_set"].."'"..val.."'";
			else
				-- Failed, show error.
				message = "*** Error "..LB_Lan["lbs_lan_err"]..".";
			end;
		end;
	elseif (rule == "useadd") then
		-- User-class for "add" functions rule, check if "value" has been set.
		if (not val or not tonumber(val)) then
			-- Not set, set warning and abort.
			message = "*** "..LB_Lan["wrn"]..LB_Lan["lbs_val_err"]..".";
		else
			-- Check if class is within bounds of admin- and user-class.
			if (val <= set_cls_admin and val >= set_cls_user) then
				-- It is, set to new values
				set_cls_add = val;
			elseif (val > set_cls_admin) then
				-- Bigger, set to admin-class level.
				set_cls_add = set_cls_admin;
			else
				-- Smaller, set to user-class level.
				set_cls_add = set_cls_user;
			end;

			-- Show message for successful change.
			message = LB_Lan["lbs_add_set"].."'"..set_cls_add.."'";
		end;
	else
		-- No, or unknown, rule name passed: Show warning.
		message = LB_Lan["err"].." "..LB_Lan["lbs_err"]..".";
	end;

	-- Send message to user.
	MsgUser(user, message);
end;

-- Change the default language of ListBot.
function SetLan(lang)
	-- Define local variables.
	local len, file;

	-- Get the length of the language code.
	len = string.len(lang);

	-- Test the length to see if just right..
	if (len > 3 or string.len(lang) < 1) then
		-- Shorter or longer than accepted, return false.
		return false;
	end;

	-- Create filename.
	file = "listbot_"..lang..".lua.inc";

	-- Check if the file exists.
	if (not io.open(file, "r")) then
		-- Close file, to avoid memory leaks.
		io.close(file);

		-- File doesn't exist, return false.
		return false;
	end;

	-- Close file, to avoid memory leaks.
	io.close(file);

	-- Run the file, and define the new default language.
	dofile(file);

	-- Return successfully.
	return true;
end;

-- Displays user's Top-10 list
function ListTop(user, list)
	-- Declare local variables, for later use.
	local sql, db_fields, res, rows, err, rows;
	local f, r = {}, {};

	-- Retrieve and check if spesific user's list was requested.
	_, _, list = string.find (list, "ltop%s([^;\"%$%|]*)");
	if (not list or list == "") then
		-- No parametre passed, set list to current user.
		list = user
	end;

	-- Retrieve Top-10 list from database.
	db_fields = "f1, r1, f2, r2, f3, r3, f4, r4, f5, r5, f6, r6, f7, r7, f8, r8, f9, r9, f0, r0";
	sql = "SELECT "..db_fields.." FROM "..set_db_top.." WHERE nick=\""..list.."\"";
	res, rows = SQ(sql);

	-- Check if query was successful.
	if (not res) then
		-- Failed, show error and abort.
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_t10_ret"].."\r\n"..
				LB_Lan["err_msg"]..rows.."\r\n"..LB_Lan["err_sql"]..sql;
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_t10_lst"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	elseif (rows == 0) then
		-- No rows, show warning and abort.
		MsgPm(set_bot_name, user, LB_Lan["wrn"]..LB_Lan["wrn_t10_no"].."\""..list.."\"");
		return 1;
	end;

	-- Retrieve the results.
	res, f[1], r[1], f[2], r[2], f[3], r[3], f[4], r[4], f[5], r[5], f[6], r[6], f[7], r[7], f[8], r[8], f[9], r[9], f[10], r[10] = SF(0);

	-- Check if retrieval was successful.
	if (not res) then
		-- Failed, show error and abort.
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_t10_qry"].."\r\n"..
				LB_Lan["err_msg"]..file[1].."\r\n"..LB_Lan["err_sql"]..sql;
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_t10_lst"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	end;

	-- TODO:
	-- Sort arrays based upon rank.

	-- Start building list.
	message = "\r\n"..set_str_lim.."\r\n\t*** "..LB_Lan["t10_list"].." '"..list.."':\r\n"..set_str_lim.."\r\n";

	-- Run through the files.
	for run = 1, 10 do
		-- Declare block-local variables.
		local ft, ti, tn, fn, fp, fc, ad, da;

		-- Cast File ID to number.
		f[run] = tonumber(f[run]);

		-- Check if file data is set, and if file has correct ID.
		if (f[run] and f[run] ~= 0) then
			-- File is set, retrieve file details.
			sql = "SELECT f.title, f.type, t.type, f.file, f.path, f.comment, f.adder, f.dateadd FROM "..set_db_table.." AS f "..
				"LEFT JOIN "..set_db_types.." AS t ON t.id = f.type WHERE f.id="..f[run];
			res, val = SQ(sql);

			-- Check if retrieval was successful.
			if (not res) then
				-- Failed, sending error to user and admins.
				message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_t10_lst"].."\r\n"..
						LB_Lan["err_msg"]..val.."\r\n"..LB_Lan["err_sql"]..sql;
				MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_t10_lst"]);
				MsgOps(set_bot_name, message, 5);
				return false;
			end;

			-- Get row from table.
			_, ft, ti, tn, fn, fp, fc, ad, da = SF(0);

			-- Check if path is set.
			if (not fp) then
				-- Path is not set, set to empty string to avoid errors.
				fp = "";
			elseif (set_wh_use) then
				-- Path is set and web-path is to be used, verify that "path" conforms with syntax.
				if (string.find(fp, "\\", 1, 1)) then
					-- Does not, replace all backslashes into slash.
					path = string.gsub(fp, "\\", "/");
				end;

				-- Check if path starts with a slash.
				if (string.sub(fp, 1, 1) ~= "/") then
					-- Does not, add one.
					fp = "/"..fp;
				end;

				-- Check to see if path ends witha slash.
				if (string.sub(fp, -1) ~= "/") then
					-- Does not, add one.
					fp = fp.."/";
				end;

				-- Checks to see if filename starts with either slash.
				if (string.sub(fn, 1, 1) == "/" or string.sub(fn, 1, 1) == "\\") then
					-- Does, remove it.
					fn = string.sub(fn, 2);
				end;

				-- Build the URL line.
				url = fp.."\r\n\t"..LB_Lan["url"].."http://"..set_wh_path..fp..fn;
				fp = url;
			end;

			-- Check if comment is set.
			if (not fc) then
				-- Comment is not set, set to empty string to avoid errors.
				fc = "";
			end;

			-- Check if string type was retrieved.
			if (not tn or tn == "") then
				-- String type not set, set to ID for missing type.
				tn = ti;
			end;

			-- Add file to list.
			message = message..LB_Lan["rank"]..r[run]..":  "..ft.."\t"..LB_Lan["type"]..tn.."\t"..LB_Lan["poster"]..
					"[ "..ad.." ]\r\n\t"..LB_Lan["fid"]..f[run].."\r\n\t"..LB_Lan["added"]..ad.."\r\n\t"..LB_Lan["fname"]..
					fn.."\r\n\t"..LB_Lan["path"]..fp.."\r\n\t"..LB_Lan["cmnt"]..fc.."\r\n\r\n";
		end;
	end;

	-- Send message, and return successfully.
	MsgPm(set_bot_name, user, message..set_str_lim);
	return 1;
end;

-- Adds given file to users Top-10 list.
function AddTop(user, data)
	-- Define local variables, for later use.
	local message, sql, rows, err, res, fid, score, found, pos;
	local file = {};

	-- Check if user has access to Top-10 classs.
	if (not IsClass(user, set_cls_top)) then
		-- User lacks access, show warning and abort.
		MsgPm(set_bot_name, LB_Lan["wrn"]..LB_Lan["wrn_t10_acc"]);
		return 1;
	end;

	-- Split file-id and score from data.
	_, _, fid, score = string.find(data, "(%d+)%s(%d)");

	-- Check if file ID and new score has been set.
	if (not fid or not score) then
		-- Not set, send warning to user and abort.
		MsgPm(set_bot_name, user, LB_Lan["wrn"]..LB_Lan["wrn_t10_mis"]);
		return 1;
	end;

	-- Retrieve details on existing list.
	sql = "SELECT f1, f2, f3, f4, f5, f6, f7, f8, f9, f0 FROM "..set_db_top.." WHERE nick=\""..user.."\"";
	res, rows = SQ(sql);

	-- Check if retrieval was successful.
	if (not res) then
		-- Failed, show error and abort.
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_t10_ret"].."\r\n"..
				LB_Lan["err_msg"]..rows.."\r\n"..LB_Lan["err_sql"]..sql;
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_t10_add"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	end;

	-- Check if user has an existing list.
	if (rows == 0) then
		-- No existing list, set SQL to create it.
		sql = "INSERT INTO "..set_db_top.." (nick, f1, r1) VALUES (\""..user.."\", "..fid..", "..score..")";
	else
		-- Existing list found, retrieve the results.
		res, file[1], file[2], file[3], file[4], file[5], file[6], file[7], file[8], file[9], file[10] = SF(0);

		-- Check if retrieval was successful.
		if (not res) then
			-- Failed, show error and abort.
			message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_t10_qry"].."\r\n"..
					LB_Lan["err_msg"]..file[1].."\r\n"..LB_Lan["err_sql"]..sql;
			MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_t10_add"]);
			MsgOps(set_bot_name, message, 5);
			return false;
		end;

		-- Run through results, and check if there is an available spot.
		for run = 1, 10 do
			-- Cast file ID to number.
			file[run] = tonumber(file[run]);

			-- Check if file ID is set.
			if (not file[run] or file[run] == 0) then
				-- Flag available spot found, and break loop.
				pos = run;
				found = true;
				break;
			end;
		end;

		-- Check if available spot was found.
		if (not found) then
			-- Not found, show warning and abort.
			MsgPm(set_bot_name, LB_Lan["wrn"]..LB_Lan["wrn_t10_ful"]);
			return 1;
		end;

		-- Verify pos.
		if (pos == 10) then
			-- Pos is 10, set to 0 for compability.
			pos = 0;
		end;

		-- Set SQL to insert data at next available spot.
		sql = "UPDATE "..set_db_top.." SET f"..pos.."="..fid..", r"..pos.."="..score.." WHERE nick=\""..user.."\"";
	end;

	-- Add new file to user's Top-10 list.
	res, err = SQ(sql);

	-- Check if addition was successful.
	if (not res) then
		-- Failed, show error and abort.
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_t10_ins"].."\r\n"..
				LB_Lan["err_msg"]..err.."\r\n"..LB_Lan["err_sql"]..sql;
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_t10_add"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	end;

	-- Show confirmation message, and return successfully.
	MsgPm(set_bot_name, user, LB_Lan["success"]..LB_Lan["t10_suc_1"]..fid.." "..LB_Lan["t10_suc_2"].." '"..score.."'.");
	return 1;
end;

-- Changes the score of given file in users Top-10 list.
function RankTop(user, data)
	-- Define local variables, for later use.
	local message, sql, err, res, rows, fid, score, found, pos;
	local file = {};

	-- Check if user has access to Top-10 classs.
	if (not IsClass(user, set_cls_top)) then
		-- User lacks access, show warning and abort.
		MsgPm(set_bot_name, LB_Lan["wrn"]..LB_Lan["wrn_t10_acc"]);
		return 1;
	end;

	-- Split file-id and score from data.
	_, _, fid, score = string.find(data, "(%d+)%s(%d)");

	-- Check if file ID and new score has been set.
	if (not fid or not score) then
		-- Not set, send warning to user and abort.
		MsgPm(set_bot_name, user, LB_Lan["wrn"]..LB_Lan["wrn_t10_mis"]);
		return 1;
	end;

	-- Get user's list details from Top-10 table..
	sql = "SELECT f1, f2, f3, f4, f5, f6, f7, f8, f9, f0 FROM "..set_db_top.." WHERE nick=\""..user.."\"";
	res, rows = SQ(sql);

	-- Check if query was successful, and user's list exists.
	if (not res) then
		-- Failed, show error and abort.
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_t10_ret"]..".\r\n"..
				LB_Lan["err_msg"]..rows.."\r\n"..LB_Lan["err_sql"]..sql;
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_t10_scr"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	elseif (rows == 0) then
		-- No rows, show warning and abort.
		MsgPm(set_bot_name, user, LB_Lan["wrn"]..LB_Lan["wrn_t10_non"]);
		return 1;
	end;

	-- Retrieve the results.
	res, file[1], file[2], file[3], file[4], file[5], file[6], file[7], file[8], file[9], file[0] = SF(0);

	-- Check if retrieval was successful.
	if (not res) then
		-- Failed, show error and abort.
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_t10_qry"].."\r\n"..
				LB_Lan["err_msg"]..file[1].."\r\n"..LB_Lan["err_sql"]..sql;
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_t10_scr"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	end;

	-- Set default message.
	message = LB_Lan["wrn"]..LB_Lan["wrn_t10_fid"];

	-- Check if File ID is in user's Top-10 list.
	for run = 0, 9 do
		-- Check if file data is set, and if file has correct ID.
		if (file[run] and file[run] ~= "" and file[run] == fid) then
			-- Flag file to be found, and break loop.
			pos = run;
			found = true;
			break;
		end;
	end;

	-- Check if the file ID was found.
	if (found) then
		-- Found, set new score.
		sql = "UPDATE "..set_db_top.." SET r"..pos.."="..score.." WHERE nick=\""..user.."\"";
		res, err = SQ(sql);

		-- Check if update succeeded.
		if (not res) then
			-- Failed, show error and abort.
			message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_t10_"].."\r\n"..
					LB_Lan["err_msg"]..err.."\r\n"..LB_Lan["err_sql"]..sql;
			MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_t10_scr"]);
			MsgOps(set_bot_name, message, 5);
			return false;
		end;

		-- Update message to show success.
		message = LB_Lan["success"]..LB_Lan["t10_upd_1"]..fid.." "..LB_Lan["t10_upd_1"].." '"..score.."'.";
	end;

	-- Show message, and return.
	MsgPm(set_bot_name, user, message);
	return 1;
end;

-- Removes a file from user's Top-10 list.
function DelTop(user, fid)
	-- Define local variables, for later use.
	local message, sql, err, rows, res, pos;
	local file = {};

	-- Check if user has access to Top-10 classs.
	if (not IsClass(user, set_cls_top)) then
		-- User lacks access, show warning and abort.
		MsgPm(set_bot_name, LB_Lan["wrn"]..LB_Lan["wrn_t10_acc"]);
		return 1;
	end;

	-- Convert fid to number.
	fid = tonumber(fid);

	-- Check if file ID has been set.
	if (not fid) then
		-- Not set, send warning to user and abort.
		MsgPm(set_bot_name, user, LB_Lan["wrn"]..LB_Lan["wrn_t10_mis"]);
		return 1;
	end;

	-- Get user's list details from Top-10 table..
	sql = "SELECT f1, f2, f3, f4, f5, f6, f7, f8, f9, f0 FROM "..set_db_top.." WHERE nick=\""..user.."\"";
	res, rows = SQ(sql);

	-- Check if query was successful, and user's list exists.
	if (not res) then
		-- Failed, show error and abort.
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_t10_ret"].."\r\n"..
				LB_Lan["err_msg"]..rows.."\r\n"..LB_Lan["err_sql"]..sql;
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_t10_del"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	elseif (rows == 0) then
		-- No rows, show warning and abort.
		MsgPm(set_bot_name, user, LB_Lan["wrn"]..LB_Lan["wrn_t10_non"]);
		return 1;
	end;

	-- Retrieve the results.
	res, file[1], file[2], file[3], file[4], file[5], file[6], file[7], file[8], file[9], file[10] = SF(0);

	-- Check if retrieval was successful.
	if (not res) then
		-- Failed, show error and abort.
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_t10_qry"].."\r\n"..
				LB_Lan["err_msg"]..file[1].."\r\n"..LB_Lan["err_sql"]..sql;
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_t10_del"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	end;

	-- Set default message.
	message = LB_Lan["wrn"]..LB_Lan["wrn_t10_fid"];

	-- Run through the files.
	for run = 1, 10 do
		-- Check if file data is set, and if file has correct ID.
		if (file[run] and tonumber(file[run]) == fid) then
			if (run == 10) then
				pos = 0;
			else
				pos = run;
			end;

			-- File is found, remove it from the table.
			sql = "UPDATE "..set_db_top.." SET f"..pos.."=NULL, r"..pos.." = 0 WHERE nick=\""..user.."\"";
			res, err = SQ(sql);

			-- Check if removal was successful.
			if (not res) then
				-- Removal failed, send error to user and admins.
				message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_t10_del"].."\r\n"..
						LB_Lan["err_msg"]..val.."\r\n"..LB_Lan["err_sql"]..sql;
				MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_t10_del"]);
				MsgOps(set_bot_name, message, 5);
				return false;
			end;

			-- Update message to show success.
			message = LB_Lan["success"]..LB_Lan["t10_del_1"]..fid.." "..LB_Lan["t10_del_2"];
			break;
		end;
	end;

	-- Send message, and return successfully.
	MsgPm(set_bot_name, user, message);
	return 1;
end;

-- Send list over users who have added files to "user".
function ListUsers(user)
	-- Define local variables;
	local message, sql, res, rows, nick;

	-- Retrieve list of users that have added files.
	sql = "SELECT adder FROM "..set_db_table.." GROUP BY adder ORDER BY adder";
	res, rows = SQ(sql);

	-- Check if retrieval was successful.
	if (not res) then
		-- Failed, show error to user & admins.
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_usr_ret"].."\r\n"..
				LB_Lan["err_msg"]..val.."\r\n"..LB_Lan["err_sql"]..sql;
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_usr_lst"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	end;

	-- Start building list.
	message = "\r\n"..set_str_lim.."\r\n*** "..LB_Lan["user_list"].."\r\n"..set_str_lim.."\r\n";

	-- Spool through results, and send them to the user.
	for run = 0, val - 1 do
		res, nick = SF(run);
		message = message.."\t"..nick.."\r\n";
	end;

	-- Send the list to, and return successfully.
	MsgPm(set_bot_name, user, message..set_str_lim);
	return 1;
end;

-- Send list of files added by "nick" to "user".
function UserFiles(user, nick)
	-- Define local variables, for later use.
	local message, sql, ttype;

	-- Validate that "nick" contains only legal input, and was submitted.
	if (not nick or nick == '' or string.find(nick, "[%|%$%;\"]")) then
		-- No (valid) nick, send warning to user.
		MsgPm(set_bot_name, user, LB_Lan["wrn"]..LB_Lan["wrn_usr_nck"].." &#124; $ ; \"");
		return 1;
	end;

	-- All files requested, build SQL-query.
	sql = "SELECT f.id, t.type, f.title, f.file, f.path, f.comment, f.adder, f.dateadd FROM "..set_db_table.." AS f LEFT JOIN "..
		set_db_types.." AS t ON f.type=t.id WHERE f.adder='"..nick.."' ORDER BY t.type, f.title";

	-- Set correct type for later on.
	ttype = "user";

	-- Call function to show list, and test if it was successful.
	if (ShowList(user, sql, ttype, nick)) then
		-- Return successfully.
		return 1;
	end;

	-- Failed, return false.
	return false;
end;

-- Send list over types to "user".
function ListTypes(user)
	-- Define local variables, for later use.
	local res, val, sql, message, tid, fn, count;

	-- Retrieve data from database.
	sql = "SELECT type, id, count FROM "..set_db_types.." ORDER BY id ASC";
	res, val = SQ(sql);

	-- Check if retrieval was successful.
	if (not res) then
		-- Failed, show error to user & admins.
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_typ_ret"].."\r\n"..
				LB_Lan["err_msg"]..val.."\r\n"..LB_Lan["err_sql"]..sql;
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_typ_lst"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	end;

	-- Check if there are any file-types available.
	if (val == 0) then
		-- None available, notify user user and return successfuly.
		MsgPm(set_bot_name, user, LB_Lan["no_types"]);
		return 1;
	end;

	-- Start building list.
	message = "\r\n"..set_str_lim.."\r\n*** "..LB_Lan["typ_list"].."\r\n"..set_str_lim.."\r\n";

	-- Spool through results, and send them to the user.
	for run = 0, val - 1 do
		res, fn, tid, cn = SF(run);
		message = message.."** "..tid.."\t"..fn.."\t("..cn..")\r\n";
	end;

	-- Send details to user, and return successfully.
	MsgPm(set_bot_name, user, message..set_str_lim);
	return 1;
end;

-- Delete types from database.
function DelType(user, ft)
	-- Define local variables, for later use.
	local res, val, sql, message, f_type, found;

	-- Check if ft is set.
	if (not ft or ft == "" or ft == 0) then
		-- File-type not set, send warning to user.
		MsgPm(set_bot_name, user, LB_Lan["wrn"]..LB_Lan["wrn_typ_mis"]);
		return 1;
	end;

	-- Check if sent file-type is string.
	sql = "SELECT id FROM "..set_db_types.." WHERE type='"..ft.."'";
	res, val = SQ(sql);

	if (not res or val == 0) then
		-- Check if file-type is ID number.
		found, _, f_type = string.find(ft, "(%d+)");
		if (not found) then
			-- Not valid file-type, send warning to user.
			MsgPm(set_bot_name, user, LB_Lan["wrn"]..LB_Lan["wrn_typ_mis"]);
			return 1;
		end;

		-- Check if ID exists.
		sql = "SELECT id FROM "..set_db_types.." WHERE id="..f_type;
		res, val = SQ(sql);
		if (not res or val == 0) then
			-- ID doesn't exists, send warning to user.
			MsgPm(set_bot_name, user, LB_Lan["wrn"]..LB_Lan["wrn_typ_mis"]);
			return 1;
		end;
	end;

	-- Get ID to delete.
	res, val = SF(0);

	-- File-type exists, delete from table.
	sql = "DELETE FROM "..set_db_types.." WHERE id="..val;
	res, err = SQ(sql);

	-- Check if deletion was successful.
	if (not res) then
		-- Deletion failed, send error to user and admins.
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_typ_del"].."\r\n"..
				LB_Lan["err_msg"]..val.."\r\n"..LB_Lan["err_sql"]..sql;
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_typ_del"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	end;

	-- Check if there are any files to be deleted.
	if (val ~= 0) then
		-- Delete all files of deleted type.
		sql = "DELETE FROM "..set_db_table.." WHERE type="..val;
		res, err = SQ(sql);

		-- Check if deletion was successful.
		if (not res) then
			-- Deletion failed, send error to user and admins.
			message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_typ_del"].."\r\n"..
					LB_Lan["err_msg"]..err.."\r\n"..LB_Lan["err_sql"]..sql;
			MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_typ_del"]);
			MsgOps(set_bot_name, message, 5);
			return false;
		end;
	end;

	-- Check if alert all admins.
	if (CheckAlert() == true) then
		-- Alert admins set, send message.
		MsgOps(set_bot_name, "** "..LB_Lan["user"].." \""..user.."\" "..LB_Lan["op_typ_del"].." \""..ft.."\".", 5);
	end;

	-- Show confirmation message, and return successfully.
	MsgPm(set_bot_name, user, LB_Lan["typ_del"]);
	return 1;
end;

-- Add new type to database.
function AddType(user, ft)
	-- Define local variables, for later use.
	local res, val, err, sql, message;

	-- Check if File-type is set.
	if (ft == "") then
		-- Not set / valid, show error
		MsgPm(set_bot_name, user, LB_Lan["wrn"]..LB_Lan["wrn_typ_mis"]);
		return 1;
	end;

	-- Retrieve file-type from database, if it exists.
	sql = "SELECT * FROM "..set_db_types.." WHERE type='"..ft.."'";
	res, val = SQ(sql);

	-- Check if retrieval was successful.
	if (not res) then
		-- Retrieval failed, show error and alert admins.
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_typ_chk"].."\r\n"..
				LB_Lan["err_msg"]..err.."\r\n"..LB_Lan["err_sql"]..sql;
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_typ_add"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	end;

	-- Check if type exists already.
	if (val ~= 0) then
		 -- Type already exists, show warning.
		 MsgPm(set_bot_name, user, LB_Lan["wrn"]..LB_Lan["wrn_typ_ext"]);
		 return 1;
	end;

	-- Add file-type to table.
	sql = "INSERT INTO "..set_db_types.." (type) VALUES ('"..ft.."')";
	res, err = SQ(sql);

	-- Check if successful.
	if (not res) then
		-- Not successful, show error to user & admins.
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_typ_sav"]..ft..".\r\n"..
				LB_Lan["err_msg"]..val.."\r\n"..LB_Lan["err_sql"]..sql;
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_typ_add"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	end;

	-- Check if alert all admins.
	if (CheckAlert() == true) then
		-- Alert admins set, send message.
		MsgOps(set_bot_name, "** "..LB_Lan["user"].." \""..user.."\" "..LB_Lan["op_typ_add"].." \""..ft.."\".", 5);
	end;

	-- Show confirmation message, and return successfully.
	MsgPm(set_bot_name, user, LB_Lan["typ_saved"]);
	return 1;
end;

-- Change the type of a file.
function ChangeType(user, fid, tid)
	-- Define local variables, for later use.
	local sql, res, err, rows, message;

	-- Check if file ID and new type ID has been set.
	if (not fid or not tid) then
		-- Not set, send warning to user and abort.
		MsgPm(set_bot_name, user, LB_Lan["wrn"]..LB_Lan["wrn_typ_mis"]);
		return 1;
	end;

	-- Check if new type ID exists.
	sql = "SELECT id FROM "..set_db_types.." WHERE id="..tid;
	res, rows = SQ(sql);

	-- Check if query was successful.
	if (not res) then
		-- Failed, show error and abort.
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_typ_new"].."\r\n"..
				LB_Lan["err_msg"]..rows.."\r\n"..LB_Lan["err_sql"]..sql;
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_typ_cng"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	elseif (rows == 0) then
		-- No results found, show warning and abort.
		MsgPm(set_bot_name, user, LB_Lan["wrn"]..LB_Lan["wrn_typ_mis"]);
		return 1;
	end;

	-- Get current type ID.
	sql = "SELECT type FROM "..set_db_table.." WHERE id="..fid;
	res, err = SQ(sql);

	-- Check if query was successful.
	if (not res) then
		-- Failed, show error and abort.
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_typ_old"].."\r\n"..
				LB_Lan["err_msg"]..err.."\r\n"..LB_Lan["err_sql"]..sql;
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_typ_cng"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	end;

	-- Retrieve SQL result.
	res, val = SF(0);

	-- Check if result was fetched successfully.
	if (not res) then
		-- Failed, show error and abort.
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_typ_ors"].."\r\n"..
				LB_Lan["err_msg"]..val.."\r\n"..LB_Lan["err_sql"]..sql;
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_typ_cng"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	end;

	-- Set new type + 1.
	sql = "UPDATE "..set_db_types.." SET count=count+1 WHERE id="..tid;
	res, err = SQ(sql);

	-- Check if result was fetched successfully.
	if (not res) then
		-- Failed, show error and abort.
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_typ_inc"].."\r\n"..
				LB_Lan["err_msg"]..err.."\r\n"..LB_Lan["err_sql"]..sql;
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_typ_cng"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	end;

	-- Set current type - 1.
	sql = "UPDATE "..set_db_types.." SET count=count-1 WHERE id="..val;
	res, err = SQ(sql);

	-- Check if result was fetched successfully.
	if (not res) then
		-- Failed, show error and abort.
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_typ_dec"].."\r\n"..
				LB_Lan["err_msg"]..err.."\r\n"..LB_Lan["err_sql"]..sql;
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_typ_cng"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	end;

	-- Build and execute SQL-query.
	sql = "UPDATE "..set_db_table.." SET type="..tid.." WHERE id="..fid;
	res, err = SQ(sql);

	-- Check if query was successful.
	if (not res) then
		-- Failed, show error and abort.
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_typ_upd"].."\r\n"..
				LB_Lan["err_msg"]..val.."\r\n"..LB_Lan["err_sql"]..sql;
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_typ_cng"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	end;

	-- Show confirmation message.
	MsgPm(set_bot_name, user, LB_Lan["typ_cng_1"].."'"..fid.."' "..LB_Lan["typ_cng_2"]..tid..".");
end;

-- Show help-message to user.
function MsgHelp (user, prefix)
	-- Define local variables, for later use.
	local help;

	-- Build help-message.
	help = "\r\n"..set_str_lim..set_str_lim.."\r\n"..LB_Lan["h_header"].."\r\n"..
		prefix..set_cmd_help.."\t\t"..LB_Lan["h_help"].."\r\n"..
		prefix.."list <type_id>\t"..LB_Lan["h_ltype"].."\r\n"..
		prefix.."list types\t\t"..LB_Lan["h_ltypes"].."\r\n"..
		prefix.."list new\t\t"..LB_Lan["h_lnew"].."\r\n"..
		prefix.."list all\t\t"..LB_Lan["h_lall"].."\r\n"..
		prefix.."list range <S> <E>\t"..LB_Lan["h_lrange"].."\r\n\r\n"..
		prefix.."ltop [nick]\t"..LB_Lan["h_ltop"].."\r\n";

	-- Check if user has access to Top-10 commands.
	if (IsClass(user, set_cls_top)) then
		-- User has access, add help for Top-10 manipulation commands.
		help = help..prefix.."atop <fid> <rank>\t"..LB_Lan["h_atop"].."\r\n"..
			prefix.."rtop <fid> <rank>\t"..LB_Lan["h_rtop"].."\r\n"..
			prefix.."dtop <fid>\t"..LB_Lan["h_dtop"].."\r\n";
	end;

	-- Check if user has access to "add" commands.
	if (IsClass(user, set_cls_admin) or IsClass(user, set_cls_add)) then
		help = help.."\r\n"..prefix.."ntype <type>\t"..LB_Lan["h_ntype"].."\r\n";
	end;

	-- Check if user has access to commands admin-commands.
	if (IsClass(user, set_cls_admin)) then
		-- User has access, add help for admin-functions.
		help = help..prefix.."dtype <type_id>\t"..LB_Lan["h_dtype"].."\r\n"..
			prefix.."ctype <fid> <tid>\t"..LB_Lan["h_ctype"].."\r\n"..
			prefix.."fadd <type_id> <title> <filename> [path] [comment]\r\n"..LB_Lan["h_fadd"]..
			prefix.."fdel <file_id>\t"..LB_Lan["h_fdel"]..prefix.."fadd.\r\n";
	end;

	-- Check if user is in HUB admin-class, or greater.
	if (IsClass(user, 5)) then
		-- User has access, add config command help.
		help = help.."\r\n"..prefix.."lbs <option>\t"..LB_Lan["h_lbs"].."\r\n";
	end;

	-- Delimit string, accordingly to format.
	help = help..set_str_lim..set_str_lim;

	-- Send help message to user, and return successfully.
	MsgPm(set_bot_name, user, help);
	return 1;
end;

-- List the 10 newest files.
function ListNew(user)
	-- Define local variables, for later use.
	local val, res, sql, run, url, message;
	local id, ft, tn, fn, fp, fc, ad, ti, da;

	-- Retrieve files from database, use "JOIN LEFT" for type.
	sql = "SELECT f.id, f.title, f.type, t.type, f.file, f.path, f.comment, f.adder, f.dateadd FROM "..set_db_table.." AS f "..
		"LEFT JOIN "..set_db_types.." AS t ON t.id = f.type ORDER BY id DESC LIMIT "..set_no_news;
	res, val = SQ(sql);

	-- Check if retrieval was successful.
	if (not res) then
		-- Failed, sending error to user and admins.
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_fil_lst"].."\r\n"..
				LB_Lan["err_msg"]..val.."\r\n"..LB_Lan["err_sql"]..sql;
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_fil_lst"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	end;

	-- Check if any files have been added.
	if (val == 0) then
		 -- No files added, return notice to user.
		 MsgPm(set_bot_name, user, "*** "..LB_Lan["no_files"]);
		 return 1;
	end;

	-- Start building list.
	message = "\r\n"..set_str_lim.."\r\n\t*** "..LB_Lan["showing"].." "..set_no_news.." "..LB_Lan["new_list"]..":\r\n"..set_str_lim.."\r\n";

	-- Spool through the results, and PM them to the requesting. With URL.
	for run = 0, val - 1 do
		-- Get row from table.
		_, id, ft, ti, tn, fn, fp, fc, ad, da = SF(run);

		-- Check if path is set.
		if (not fp) then
			-- Path is not set, set to empty string to avoid errors.
			fp = "";
		elseif (set_wh_use) then
			-- Path is set and web-path is to be used, verify that "path" conforms with syntax.
			if (string.find(fp, "\\", 1, 1)) then
				-- Does not, replace all backslashes into slash.
				path = string.gsub(fp, "\\", "/");
			end;

			-- Check if path starts with a slash.
			if (string.sub(fp, 1, 1) ~= "/") then
				-- Does not, add one.
				fp = "/"..fp;
			end;

			-- Check to see if path ends witha slash.
			if (string.sub(fp, -1) ~= "/") then
				-- Does not, add one.
				fp = fp.."/";
			end;

			-- Checks to see if filename starts with either slash.
			if (string.sub(fn, 1, 1) == "/" or string.sub(fn, 1, 1) == "\\") then
				-- Does, remove it.
				fn = string.sub(fn, 2);
			end;

			-- Build the URL line.
			url = fp.."\r\n\t"..LB_Lan["url"].." http://"..set_wh_path..fp..fn;
			fp = url;
		end;

		-- Check if comment is set.
		if (not fc) then
			-- Comment is not set, set to empty string to avoid errors.
			fc = "";
		end;

		-- Check if string type was retrieved.
		if (not tn or tn == "") then
			-- String type not set, set to ID for missing type.
			tn = ti;
		end;

		-- Send details to user.
		message = message..LB_Lan["file"]..id..":    "..ft.."\t"..LB_Lan["type"]..tn.."\t"..LB_Lan["poster"]..
				" [ "..ad.." ]\r\n\t"..LB_Lan["added"]..da.."\r\n\t"..LB_Lan["fname"]..fn.."\r\n\t"..
				LB_Lan["path"]..fp.."\r\n\t"..LB_Lan["cmnt"]..fc.."\r\n\r\n";
	end;

	-- Send details to user, and return successfully.
	MsgPm(set_bot_name, user, message..set_str_lim);
	return 1;
end;

-- List all files from database of type "ft".
function ListFiles(user, ft)
	-- Declare local variables, for later use.
	local sql, res, val, f_type, ttype, min, max;

	-- Check if file type is set.
	if (ft == "" or not ft) then
		-- File-type not set, sending error.
		MsgPm(set_bot_name, user, LB_Lan["wrn"]..LB_Lan["wrn_typ_mis"]);
		return false;
	end;

	-- Check what type of query was used.
	if (ft == "all") then
		-- All files requested, build SQL-query.
		sql = "SELECT f.id, t.type, f.title, f.file, f.path, f.comment, f.adder, f.dateadd FROM "..set_db_table.." AS f LEFT JOIN "..
			set_db_types.." AS t ON f.type=t.id ORDER BY t.type, f.title";

		-- Set correct type for later on.
		ttype = "all";
	elseif (string.find(ft, "range", 1, 1)) then
		-- Range requested, get lower and upper limit.
		_, _, min, max = string.find(ft, "range%s(%d+)%s(%d+)");

		-- Check if ranges are valid.
		if (not(min and min ~= "" and max and max ~= "")) then
			-- Ranges now valid, show warning to user and abort.
			MsgPm(set_bot_name, user, LB_Lan["wrn"]..LB_Lan["wrn_fil_rng"]);
			return false;
		end;

		-- Build SQL-query.
		sql = "SELECT f.id, t.type, f.title, f.file, f.path, f.comment, f.adder, f.dateadd FROM "..set_db_table.." AS f LEFT JOIN "..
			set_db_types.." AS t ON f.type=t.id WHERE f.id>="..min.." AND f.id<="..max.." ORDER BY t.type, f.title";

		-- Set correct type for later on.
		ttype = "range";
	else
		-- Type listing requested, check if file-type given is number.
		_, _, f_type = string.find(ft, "(%d+)");

		if (f_type and tonumber(ft) == tonumber(f_type)) then
			-- File type is ID, build SQL sentence.
			sql = "SELECT id, type FROM "..set_db_types.." WHERE id="..f_type;
		else
			-- File type is name, build SQL sentence.
			sql = "SELECT id, type FROM "..set_db_types.." WHERE type='"..ft.."'";
		end;

		-- Check if file-type exists.
		res, val = SQ(sql);
		if (not res or val == 0) then
			-- File-type doesn't exist, show error.
			MsgPm(set_bot_name, user, LB_Lan["wrn"]..LB_Lan["wrn_typ_mis"]);
			return false;
		end;

		-- Fetch type-ID and type-name.
		res, val, ttype = SF(0);

		-- Build SQL to retrieve all items of type from database.
		sql = "SELECT id, title, file, path, comment, adder, dateadd FROM "..set_db_table.." WHERE type="..val;
	end;

	-- Call function to show list, and test if it was successful.
	if (ShowList(user, sql, ttype, "")) then
		-- Return successfully.
		return 1;
	end;

	-- Failed, return false.
	return false;
end;

-- Display the actual file-lists, info taken from "sql", "ttype" and (optionally) "nick".
function ShowList(user, sql, ttype, nick)
	-- Define local variables, for later use.
	local res, rows, message, run, id, tt, ft, fn, fp, fc, ad, da;

	-- Execute selected SQL query.
	res, val = SQ(sql);

	-- Check if retrieval was successful.
	if (not res) then
		-- Failed, sending error to user and admins.
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_fil_lst"].."\r\n"..
				LB_Lan["err_msg"]..val.."\r\n"..LB_Lan["err_sql"]..sql;
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["err_fil_lst"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	end;

	-- Check if any results were given
	if (not rows or rows == 0) then
		-- Select correct message.
		if (ttype == "all") then
			-- All files requested.
			message = "*** "..LB_Lan["no_files"];
		elseif (ttype == "range") then
			-- Range requested.
			message = LB_Lan["no_range"];
		elseif (ttype == "user") then
			-- Spesific user's files requested.
			message = LB_Lan["no_fuser"];
		else
			-- Spesific type requested
			message = LB_Lan["no_type"];
		end;

		-- No results, notify user.
		MsgPm(set_bot_name, user, message);
		return true;
	end;

	-- Begin message header.
	message = "\r\n"..set_str_lim.."\r\n*** ";

	-- Check what header to use.
	if (ttype == "range") then
		-- Range requested, build correct list.
		message = message..LB_Lan["range_list"]..min.." to "..max;
	elseif (ttype == "user") then
		-- User-added files requested.
		message = message..LB_Lan["user_files"]..nick;
	else
		-- Other list requested, build correct list.
		message = message..LB_Lan["showing"].." "..ttype.." "..LB_Lan["files"];
		tt = ttype;
	end;

	-- Complete message header.
	message = message..":\r\n"..set_str_lim.."\r\n";

	-- Run through results, and send them to the user. With URL.
	for run = 0, rows - 1 do
		-- Check what type of query was used.
		if (ttype == "all" or ttype == "range" or ttype == "user") then
			-- All types, get correct row from table.
			res, id, tt, ft, fn, fp, fc, ad, da = SF(run);
		else
			-- Spesific type, get correct row from table.
			res, id, ft, fn, fp, fc, ad, da = SF(run);
		end;

		-- Check if path is set.
		if (not fp) then
			-- Path is not set, set to empty string to avoid errors.
			fp = "";
		elseif (set_wh_use) then
			-- Path is set and web-path is to be used, verify that "path" conforms with syntax.
			if (string.find(fp, "\\", 1, 1)) then
				-- Does not, replace all backslashes into slash.
				path = string.gsub(fp, "\\", "/");
			end;

			-- Check if path starts with a slash.
			if (string.sub(fp, 1, 1) ~= "/") then
				-- Does not, add one.
				fp = "/"..fp;
			end;

			-- Check to see if path ends witha slash.
			if (string.sub(fp, -1) ~= "/") then
				-- Does not, add one.
				fp = fp.."/";
			end;

			-- Checks to see if filename starts with either slash.
			if (string.sub(fn, 1, 1) == "/" or string.sub(fn, 1, 1) == "\\") then
				-- Does, remove it.
				fn = string.sub(fn, 2);
			end;

			-- Build the URL line.
			fp = fp.."\r\n\t"..LB_Lan["url"].."http://"..set_wh_path..fp..fn;
		end;

		-- Check if comment is set.
		if (not fc) then
			-- Comment is not set, set to empty string to avoid errors.
			fc = "";
		end;

		-- Check if type string is set, and contains data.
		if (not tt or tt == "") then
			-- Failed, populate string with content.
			tt = "<N/A>";
		end;

		-- Create list for showing all files of all types.
		message = message..LB_Lan["file"]..id..":    "..ft.."\t"..LB_Lan["type"]..tt.."\t"..LB_Lan["poster"]..
				"[ "..ad.." ]\r\n\t"..LB_Lan["added"]..da.."\r\n\t"..LB_Lan["fname"]..fn.."\r\n\t"..
				LB_Lan["path"]..fp.."\r\n\t"..LB_Lan["cmnt"]..fc.."\r\n\r\n";
	end;

	-- Send details to user, and return successfully.
	MsgPm(set_bot_name, user, message..set_str_lim);
	return true;
end;

-- Deleting file with ID "id" from database.
function DelFile(user, id)
	-- Declare local variables, for later use.
	local res, val, err, sql, message, tid;

	-- Check if ID is set.
	if (id == "" or not id) then
		-- ID not set, send warning to user.
		MsgPm(set_bot_name, user, LB_Lan["wrn"]..LB_Lan["wrn_typ_mis"]);
		return false;
	end;

	-- Retrieve ID & type from database for checking purposes & counting reduction.
	sql = "SELECT id, type FROM "..set_db_table.." WHERE id="..id;
	res, val = SQ(sql);

	-- Check if extraction was successful, iow if file exists.
	if (not res or val == 0) then
		-- File didn't exist, send warning to user.
		MsgPm(set_bot_name, user, LB_Lan["wrn"]..LB_Lan["wrn_typ_mis"]);
		return false;
	end;

	-- Retrieve SQL result.
	res, id, tid = SF(0);

	-- Check if fetching of values was successful.
	if (not res or not id or not tid) then
		-- Did not retrieve all data, show error to user and admins.
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_fil_id"].."\r\n"..
				LB_Lan["err_msg"]..id.."\r\n"..LB_Lan["err_sql"]..sql.."\r\nRow: "..val;
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_fil_del"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	end;

	-- Reduce type-count by 1.
	sql = "UPDATE "..set_db_types.." SET count = count-1 WHERE id = "..tid;
	res, err = SQ(sql);

	-- Check if reduction was successful.
	if (not res) then
		-- Deletion not successful, show error to user and admins.
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_fil_dec"].."\r\n"..
				LB_Lan["err_msg"]..err.."\r\n"..LB_Lan["err_sql"]..sql;
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_fil_del"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	end;

	-- Delete file from database.
	sql = "DELETE FROM "..set_db_table.." WHERE id = "..id;
	res, err = SQ(sql);

	-- Check if deletion was successful.
	if (not res) then
		-- Deletion not successful, show error to user and admins.
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_fil_del"].."\r\n"..
				LB_Lan["err_msg"]..err.."\r\n"..LB_Lan["err_sql"]..sql;
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_fil_del"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	end;

	-- Check if alert all admins.
	if (CheckAlert() == true) then
		-- Alert admins set, send message.
		MsgOps(set_bot_name, "*** "..LB_Lan["user"].." \""..user.."\" "..LB_Lan["op_fil_del"]..id, 5);
	end;

	-- Show confirmation message, and return successfully.
	MsgPm(set_bot_name, user, "*** "..LB_Lan["file_del"]);
	return 1;
end;

-- Add file to database.
function AddFile(user, data)
	-- Define local variables, for later use.
	local values, sql, res, err, val, found, message;
	local ft, title, filename, path, comment, test;

	-- Requested file-add, sort variables and call function.
	_, _, ft, data = string.find(data, "[%+!]fadd%s(%S+)%s(.*)");
	title, filename, path, comment = ParseData(data);

	-- Save current date and time for add.
	date = os.date("*t");
	dateadd = (date.year.."-"..formatTF(date.month).."-"..formatTF(date.day).." "..formatTF(date.hour)..":"..formatTF(date.min)..":"..formatTF(date.sec));

	-- Check if file-type has been set correctly.
	if (not ft or ft == "") then
		 -- File type not set, show warning.
		 MsgPm(set_bot_name, user, LB_Lan["wrn"]..LB_Lan["wrn_typ_mis"]);
		 return 1;
	end;

	-- Check if title and file-name has been set
	if (not title or not filename or title == "" or filename == "") then
		MsgPm(set_bot_name, user, LB_Lan["wrn"]..LB_Lan["wrn_fil_nam"]);
		return 1;
	end;

	-- Check if type ID is numerical or string.
	if (tonumber(ft)) then
		-- Numerical, verify type ID from database.
		sql = "SELECT id FROM "..set_db_types.." WHERE id="..ft;
	else
		-- String, get numerical type ID from database.
		sql = "SELECT id FROM "..set_db_types.." WHERE type='"..ft.."'";
	end;

	-- execute SQL, and check if retrieval was successful.
	res, val = SQ(sql);
	if (not res) then
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_typ_get"].."\r\n"..
			LB_Lan["err_msg"]..val.."\r\n"..LB_Lan["err_sql"]..sql;
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_fil_add"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	end;

	-- Check if file-type exists.
	if (val == 0) then
		MsgPm(set_bot_name, user, LB_Lan["wrn"]..LB_Lan["wrn_typ_mis"]);
		return 1;
	end;

	-- Retrieve verified numerical type ID.
	res, test = SF(0);

	if (res and tonumber(test)) then
		ft = test;
	end;

	-- Verify user input.
	filename = StrFix(filename, user, "%\\%/");
	path = StrFix(path, user, "");
	title = StrFix(title, user, "");

	-- Validate database input.
	if (title == 0) then
		return 0;
	elseif (filename == 0) then
		return 0;
	elseif (path ~= "" and path == 0) then
		return 0;
	elseif (comment ~= "") then
		found = false;
		_, _, found = string.find(data, "([;%|%$])");
		if (found) then
			-- Comment data failed check, send error to user.
			message = LB_Lan["err"].." "..LB_Lan["uer_fil_ilg"].."; "
			MsgPm(set_bot_name, user, message.."$");
			return false;
		end;
	end;

	-- Check if comment is set.
	if (not comment) then
		-- Comment not set, set to empty string to avoid problems.
		comment = "";
	end;

	-- Replace all backslashes with forward slashes.
	if (string.find(filename, "\\", 1, 1)) then
		filename = string.gsub(filename, "\\", "/");
	end;

	-- Clean up path a bit.
	if (string.sub(filename, -1) == "/") then
		filename = string.sub(filename, 1, -2);
	end;

	-- Insert data into database.
	fields = "(type, title, file, path, comment, adder, dateadd)";
	values = "\""..ft.."\", \""..title.."\", \""..filename.."\", \""..path.."\", \""..comment.."\", \""..user.."\", \""..dateadd.."\"";
	sql = "INSERT INTO "..set_db_table.." "..fields.." VALUES ("..values..")";
	res, err = SQ(sql);

	-- Check if insertion was successful.
	if (not res) then
		-- Failed, alert user and Admins, then exit.
		-- Deletion not successful, show error to user and admins.
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_fil_sav"].."\r\n"..
				LB_Lan["err_msg"]..err.."\r\n"..LB_Lan["err_sql"]..sql;
		MsgPm(set_bot_name, user, LB_Lan["err"].."  "..LB_Lan["uer_fil_add"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	end;

	-- Increase type-count by 1.
	sql = "UPDATE "..set_db_types.." SET count = count+1 WHERE id = "..ft;
	res, err = SQ(sql);

	-- Check if increase was successful.
	if (not res) then
		-- Increase not successful, show error to user and admins.
		message = "*** "..LB_Lan["err"].."\r\n"..LB_Lan["err_fil_inc"].."\r\n"..
				LB_Lan["err_msg"]..err.."\r\n"..LB_Lan["err_sql"]..sql;
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_fil_add"]);
		MsgOps(set_bot_name, message, 5);
		return false;
	end;

	-- Check if alert all admins.
	if (CheckAlert() == true) then
		-- Alert admins set, send message.
		MsgOps(set_bot_name, "*** "..LB_Lan["user"].." \""..user.."\" "..LB_Lan["op_fil_add"].." \""..title.."\".", 5);
	end;

	-- Add succeeded. Show message to user, and return successfuly.
	MsgPm(set_bot_name, user, "*** "..LB_Lan["file_added"]);
	return 1;
end;

-- Parse the "fadd" data, and return the values as an array.
function ParseData(data)
	-- Define local variables, for later use.
	local ret, pos;
	ret = {};

	-- Check if data has been sent.
	if (not data or data == "") then
		-- Data has not been sent correctly, return false.
		return false;
	end;

	for run = 0, 4, 1 do
		-- Check if parametre is quoted.
		if (string.sub(data, 1, 1) == "\"") then
			-- Parametre quoted, run through string to find end of quoted text.
			for run2 = 2, string.len(data), 1 do
				-- Check if end of quoted string is found.
				if (string.sub(data, run2, run2) == "\"" and string.sub(data, run2 + 1, run2 + 1) == " ") then
					-- Is found, set position and break from loop.
					pos = run2;
					break;
				end;

				-- No end-quote found, set pos to string-length.
				pos = run2;
			end;

			-- Grab the text between the quotes.
			ret[run] = string.sub(data, 2, pos - 1);

			-- Remove the parametre from "data".
			_, _, data = string.find(string.sub(data, string.len(ret[run]) + 2),"\"%s+(.*)");
		else
			-- Parametre is not quoted, grab the text until the first space.
			_, _, ret[run] = string.find(data, "(%S+)");

			-- Check if end of string is found.
			if (not ret[run] or string.len(ret[run]) <= 0) then
				break;
			end;

			-- Remove the parametre from "data".
			_, _, data = string.find(string.sub(data, string.len(ret[run]) + 1),"%s(.*)");
		end;

		-- Check length of data, to see if there's more to be parsed.
		if (not data or string.len(data) <= 0) then
			-- No more to check, break loop.
			break;
		end;
	end;

	-- Return the data parsed.
	return ret[0], ret[1], ret[2], ret[3];
end;

-- Verify contents of string passed, "xb" is extra characters to disallow.
function StrFix(data, user, xb)
	-- Declare local variables, for later use.
	local denylist, err, found, denytext, temp;

	-- Check if data has been passed.
	if (not data or data == "") then
		 -- No data passed, exit without doing anyhting.
		 return "";
	end;

	-- Set up list over forbidden characters.
	denylist = "%;%?%*%:%<%>%|%$%\"\r\n\r\v\t";
	denytext = " ; ? * : < > $ \" %";

	-- Verify content of String.
	_, _, found = string.find(data, "(["..denylist..xb.."])");
	if (found) then
		-- String failed check, send error to user.
		MsgPm(set_bot_name, user, LB_Lan["err"].." "..LB_Lan["uer_fil_ilg"]..denytext..xb);
		return false;
	end;

	-- Format string, to be sql-safe.
	if (string.find(data, "\\", 1, 1)) then
		-- Get length of string.
		len = string.len(data);
		temp = "";

		-- Backslach found, escape it.
		for run = 0, len, 1 do
			-- Get the next char from string.
			char = string.sub(data, run, run);

			-- Check if next char is a slash.
			if (char == "\\") then
				-- Slash found, escape it.
				char = "\\\\";
			end;

			-- Populate string with next char.
			temp = temp..char;
		end;

		-- Restore data-string from temp.
		data = temp;
	end;

	-- Return string
	return data;
end;

-- Check if alert all admins have been activated.
function CheckAlert()
	if (not set_op_alert or set_op_alert == 0) then
		-- Don't alert.
		return false;
	end;

	-- Alert.
	return true;
end;

-- Make sure time/date field is two chars long.
function formatTF(field)
	if (tonumber(field) < 10 and string.len(field) == 1) then 
		field = "0"..field;
	end;

	return field;
end;

--|                                                      |--
--| Common Functions Library v 3.0                       |--
--|     Author: Hellkeepa (hellkeeper_1@hotmail.com)     |--
--|                                                      |--
--| CopyLeft, but please let me know if you change       |--
--| anything. Always remember to credit original author: |--
--| No-one appreciates having their work stolen. ;-)     |--
--|                                                      |--
--| Code added by:                                       |--
--|     Hurbold (Admin @ VerliHub forum)                 |--
--|                                                      |--

--|                                                      |--
--| To use these functions simple require() the file     |--
--| into your existing script, and use them like like    |--
--| nothing happened. ;-)                                |--
--| You should place a note in your readme that users    |--
--| should make sure their "LUA_PATH" system variable is |--
--| correctly set, as it would prevent the script from   |--
--| working correctly otherwise.                         |--
--|                                                      |--
--| Updated versions of this script are to be found at   |--
--| the official VerliHUB WIKI, the direct address is:   |--
--|     www.verlihub.net/wiki/?VerlihubLuaScripts        |--
--| Just scroll down a bit, and you should see it. :-)   |--
--|                                                      |--

--[[ Changelog:
-- 3.0	Added the following event handlers:
			** hub.OnUserLogout()
			** hub.Timer()
			** hub.GetChat()
			** hub.Unload()
		One HubSoft function:
			** RemoveBot()
		And the following user-function:
			** ParseMyInfo(nick)
		Thanks to Hurbold for that one. :-)

-- 2.0	Sectioned the library up a bit, and labeled the
		different sections. Also added four event handlers:
			** hub.OnOpCmd()
			** hub.OnUserCmd()
			** hub.OnUserLogin()
			** hub.GetPm()

-- 1.0	First revision, added the following functions:
			** SQ()
			** SF()
			** CloseConn()
			** MsgPm()
			** MsgUser()
			** MsgOps()
			** MsgMain()
			** AddBot()
			** GetConfig()
			** GetNickList()
			** GetIp()
			** GetHost()
			** GetMyInfo()
			** UserClass()
			** GetUserCount()
			** IsClass()
			** MenuOpt()
			** MenuPmOpt()
			** MenuDiv()
			** MenuCheck()
--]]--


--|------------------------------------------------------|--
--|                    Event handlers                    |--
--|------------------------------------------------------|--

hub = {};

-- Runs the Unload event handler for cleaning up after a script.
function UnLoad()
	if (hub.Unload ~= nil) then
		return hub.Unload();
	end;

	return 1;
end;

-- Intercepts Operator commands for the script.
function VH_OnOperatorCommand(nick, data)
	if (hub.OnOpCmd ~= nil) then
		return hub.OnOpCmd(nick, data);
	end;

	return 1;
end;

-- Intercepts User commands for the script.
function VH_OnUserCommand(nick, data)
	if (hub.OnUserCmd ~= nil) then
		return hub.OnUserCmd(nick, data);
	end;

	return 1;
end;

-- Intercepts Main Chat for the script.
function VH_OnParsedMsgChat(from, data)
	if (hub.GetChat ~= nil) then
		return hub.GetChat(from, data);
	end;

	return 1;
end;

-- Intercepts PMs for the script.
function VH_OnParsedMsgPm(from, data, to)
	if (hub.GetPm ~= nil) then
		return hub.GetPm(from, data, to);
	end;

	return 1;
end;

-- Intercepts user logins for the script.
function VH_OnUserLogin(nick)
	if (hub.OnUserLogin ~= nil) then
		return hub.OnUserLogin(nick);
	end;

	return 1;
end;

-- Intercepts user logouts for the script.
function VH_OnUserLogout(nick)
	if (hub.OnUserLogout ~= nil) then
		return hub.OnUserLogout(nick);
	end;

	return 1;
end;

-- Timer function.
function VH_OnTimer()
	if (hub.Timer ~= nil) then
		return hub.Timer();
	end;

	return 1;
end;


--|------------------------------------------------------|--
--|                  HUBSoft functions                   |--
--|------------------------------------------------------|--

-- SQL-query shorthand function.
function SQ(sql)
	return VH:SQLQuery(sql);
end;

-- SQL-fetch shorthand function.
function SF(row)
	return VH:SQLFetch(row);
end;

-- Close the connection for the given user.
function CloseConn(nick)
	return VH:CloseConnection(nick);
end;

-- Sends private messages to spesified user.
function MsgPm(botname, user, data)
	return VH:SendDataToUser("$To: "..user.." From: "..botname.." $<"..botname.."> "..data.."|", user);
end;

-- Sends message to user in main chat.
function MsgUser(botname, user, data)
	return VH:SendDataToUser("<"..botname.."> "..data.."|", user);
end;

-- Sends raw message to user.
function MsgRaw(user, data)
	return VH:SendDataToUser(data.."|", user);
end;

-- Sends PM to all Operators, or higher.
function MsgOps(botname, data, lev_min)
	return VH:SendPMToAll(data, botname, lev_min, 10);
end;

-- Sends messages to main, for users between "lev_min" and "lev_max".
function MsgMain(botname, data, lev_min, lev_max)
	return VH:SendDataToAll("<"..botname.."> "..data.."|", lev_min, lev_max);
end;

-- Adds as bot to the nicklist.
function AddBot(nick, desc, email)
	return VH:AddRobot(nick, 10, desc, "Bot ", email, "0");
end;

-- Removes a bot from the nicklist.
function RemoveBot(nick)
	return VH:DelRobot(nick);
end;

-- Returns value of config variable "var".
function GetConfig(var)
	-- Define local variables, for later use.
	local res, val;

	-- Get value of config variable "var".
	res, val = VH:GetConfig("config", var);

	-- Check if extraction was successful.
	if (not res) then
		-- Extraction failed, return empty string.
		return "";
	end;

	-- Success, return value.
	return val;
end;

-- Returns the userlist from the HUB.
function GetNickList()
	return VH:GetNickList();
end;

-- Retrieves the IP of the given user.
function GetIp(nick)
	return VH:GetUserIP(nick);
end;

-- Retrieves the hostname of the given user.
function GetHost(nick)
	return VH:GetUserHost(nick);
end;

-- Retrieves the MyINFO of the selected user.
function GetMyInfo(nick)
	return VH:GetMyINFO(nick);
end;

-- Retrieves class of user.
function UserClass(nick)
	-- Define local variables, for later use.
	local res, class;

	-- Get class of user "nick".
	res, class = VH:GetUserClass(nick);

	-- Check if retrieval was successful.
	if (res) then
		-- Return class.
		return class;
	end;

	-- Failed, return false.
	return false;
end;


--|------------------------------------------------------|--
--|                   Common functions                   |--
--|------------------------------------------------------|--

-- Parse a user's MyINFO
-- Returns the following list as an array:
	-- [0] => Client
	-- [1] => Version
	-- [2] => Mode
	-- [3] => Unreg Hubs
	-- [4] => Reg Hubs
	-- [5] => Op Hubs
	-- [6] => Total hubs
	-- [7] => Slots
	-- [8] => Share
	-- [9] => Description
	-- [10] => Speed
	-- [11] => Control flag
	-- [12] => Email.
function ParseMyInfo(nick)
	-- Define local variables, for later use
	local tempshare, myinfo, tag, desc, speed, flag, email, share, fshare;
	local client, version, mode, hubs, reghubs, ophubs, slots, thubs;

	-- Get the full MyINFO string
	_, myinfo = GetMyInfo(nick);

	-- Extract the user's tag from MyINFO first
	_, _, tag = string.find (myinfo, "^%$MyINFO %$ALL [^ ]+ [^$]*(%b<>)");

	-- If the user has no tag, give him one
	if tag == nil then
		tag = "<XX V:0.0,M:N,H:0/0/0,S:0>";

		-- Parse the rest of his MyINFO
		_, _, desc, speed, flag, email, share = string.find(myinfo, "^%$MyINFO %$ALL [^ ]+ ([^$]*)%$ $([^%c]+)([%c]+)[$]+([^$]*)%$(%S*)%$");
	else
		-- User has a tag in MyINFO, so parse the whole thing
		_, _, desc, speed, flag, email, share = string.find(myinfo, "^%$MyINFO %$ALL [^ ]+ ([^$]*)%b<>%$ $([^%c]+)([%c]+)[$]+([^$]*)%$(%S*)%$");
	end

	-- Prepare share for formatting
	tempshare = share / 1024 / 1024 / 1024;
	tempshare = string.format("%0.2f", tempshare);

	-- If the share is 6 digits or less, GB
	if string.len(tempshare) <= 6 then
		fshare = tempshare.." GB";
	else
		-- More than 6 digits, TB
		fshare = string.format("%0.2f", share / 1024).." TB";
	end

	-- Parse the user's tag, either his real one, or the one we gave him
	_, _, client, version, mode, hubs, reghubs, ophubs, slots = string.find(tag, "<(.+)V:(.+),M:([APN]),H:(%d+)%/(%d+)%/(%d+),S:(%d+)");

	-- Sum hub counts
	thubs = ((hubs+reghubs)+ophubs);
	return {client, version, mode, hubs, reghubs, ophubs, thubs, slots, fshare, desc, speed, flag, email}
end;

-- Count users online.
function GetUserCount()
	-- Define local variables, for later use.
	local count, users, nick;
	count = 0;

	-- Get list over active users.
	_, users = GetNickList();
	_, _, users = string.find(users, "%$[Nn]ick[lL]ist%s(.*)");

	-- Spool through online users.
	for nick in string.gfind(users, "([^%$%|]+)%$%$") do
		count = count + 1;
	end;

	-- Return user-count.
	return count;
end;

-- Checks if user is of required class or higher.
function IsClass(nick, class_min)
	-- Define local variables, for later use.
	local class;

	-- Get class of user "nick".
	class = UserClass(nick);

	-- Check if retrieval was successful, and class is higher than mininum class required.
	if (class and class >= class_min) then
		-- Has the needed access.
		return true;
	end;

	-- Don't have sufficient access, or failed checking.
	return false;
end;

-- Send a menu option to user.
function MenuOpt(user, mtype, context, menu, title, cmd)
	mtype, context = MenuCheck (mtype, context);
	return MsgRaw ("$UserCommand "..mtype..context..menu.."\\"..title.."$<%[mynick]> "..cmd.."&#124;|", user);
end;

-- Sends a menu option, for PM commands, to user.
function MenuPmOpt(user, sendto, mtype, context, menu, title, cmd)
	mtype, context = MenuCheck (mtype, context);
	return MsgRaw ("$UserCommand "..mtype..context..menu.."\\"..title.."$$To "..sendto.." From: %[mynick] $<%[mynick]> "..cmd.."&#124;|", user);
end;

-- Sends a menu divider to the user.
function MenuDiv(user, context)
	_, context = MenuCheck ("", context);
	return MsgRaw ("$UserCommand 0 "..context.."|", user);
end;

-- Verifies and fixes the context and type for user-command menus.
function MenuCheck(mt, mc)
	if (not mt or mt == "") then
		-- Default value if not set.
		mt = "1";
	end;

	if (not mc or mc == "") then
		-- Default value if not set.
		mc = "3";
	end;

	mt = mt.." ";
	mc = mc.." ";

	return mt, mc;
end;

--[[-- Flag values for the MenuOpt functions.
Context:
-- 1:	HUB menu.
-- 2:	User menu.
-- 4:	Search menu.
-- 8:	FileList menu.

Type:
-- 0:	Seperator.
-- 1:	Raw.
-- 2:	Raw, once per nick.
-- 255:	Del prev context menus.
--]]--

--|                                                      |--
--| ListBot v 1.5x Language file                         |--
--|     Language: English.                               |--
--|     Author: Hellkeepa (hellkeeper_1@hotmail.com)     |--
--|     Revision: 1.3, 182 sentences.                    |--
--|     Number of replacements: 427, ver 1.52            |--
--|                                                      |--

LB_Lan = {
	-- Error messages (general and admin messages):
	["err"]			=		"Error!",
	["err_msg"]		=		"Error message was: ",
	["err_sql"]		=		"SQL statement: ",
	["err_crt_tbl"]	=		"Could not create table: ",
	["err_sed_tbl"]	=		"Could not seed table: ",
	["err_t10_ret"]	=		"Could not retrieve Top-10 list from database.",
	["err_t10_qry"]	=		"Could not retrieve Top-10 query results.",
	["err_t10_lst"]	=		"Could not retrieve file-list from database.",
	["err_t10_ins"]	=		"Could not insert new Top-10 file into database.",
	["err_t10_upd"]	=		"Could not update rank of Top-10 file.",
	["err_t10_del"]	=		"Could not remove Top-10 file!",
	["err_typ_ret"]	=		"Could not retrieve file-types!",
	["err_typ_del"]	=		"Could not delete file-type!",
	["err_typ_del"]	=		"Could not delete files!",
	["err_typ_chk"]	=		"Could not retrieve file-type check data!",
	["err_typ_sav"]	=		"Could not save new file-type: ",
	["err_typ_get"]	=		"Could not retrieve type-ID from database.",
	["err_typ_new"]	=		"Could not retrieve new type ID from database.",
	["err_typ_old"]	=		"Could not retrieve old type ID from database.",
	["err_typ_ors"]	=		"Could not fetch old type ID result.",
	["err_typ_inc"]	=		"Could not increase new type count.",
	["err_typ_dec"]	=		"Could not decrease old type count.",
	["err_typ_upd"]	=		"Could not update file-type.",
	["err_fil_lst"]	=		"Could not retrieve file-list from database.",
	["err_fil_inc"]	=		"Could not increase type-count!",
	["err_fil_dec"]	=		"Count not decrease type count.",
	["err_fil_id"]	=		"Could not fetch id / type data!",
	["err_fil_del"]	=		"Could not delete file",
	["err_fil_sav"]	=		"Could not save new file data!",
	["err_usr_ret"]	=		"Could not retrive list of nicks",

	-- Error messages for users.
	["uer_t10_lst"]	=		"Failed to list Top-10 files due to SQL error, admins have been notified.",
	["uer_t10_add"]	=		"Failed to add Top-10 file due to SQL error, admins have been notified.",
	["uer_t10_scr"]	=		"Failed to update rank of Top-10 file due to SQL error, admins have been notified.",
	["uer_t10_del"]	=		"Failed to remove Top-10 file due to SQL error, admins have been notified.",
	["uer_typ_lst"]	=		"Could not retrieve types due to SQL error, admins have been notified.",
	["uer_typ_del"]	=		"Could not delete file-type due to SQL error, admins have been notified.",
	["uer_typ_add"]	=		"Could not store new type due to SQL error, admins have been notified",
	["uer_typ_cng"]	=		"Failed to change type due to SQL error, admins have been notified.",
	["uer_fil_lst"]	=		"Cound not retrieve files due to SQL error, admins have been notified.",
	["uer_fil_del"]	=		"Could not delete file due to SQL error, admins have been notified.",
	["uer_fil_add"]	=		"Could not add file due to SQL error, admins have been notified.",
	["uer_fil_ilg"]	=		"Input contains illegal charachers.\r\nPlease remove all "..
							"occourances of all the following characters, and try again: ",
	["uer_usr_lst"]	=		"Failed to list list of ListBot users due to SQL error, admins have been notified.",

	-- Warning messages (for users):
	["wrn"]			=		"Warning!",
	["wrn_not_fnd"]	=		"Command not found, please try again",
	["wrn_not_acp"]	=		"This bot will only accept certain commands, please write '!help' and try again",
	["wrn_t10_no"]	=		"Cound not find a Top-10 list for user ",
	["wrn_t10_non"]	=		"You don't seem to have a Top-10 list, please add at least one file to it before using this command.",
	["wrn_t10_acc"]	=		"You do not have sufficient access to use this command, please contact an OP if you want a Top-10 list.",
	["wrn_t10_mis"]	=		"File ID/rank not set, or invalid. Please try again.",
	["wrn_t10_ful"]	=		"No available file entry found, please delete a file and try again.",
	["wrn_t10_fid"]	=		"Could not find given file-ID in your Top-10 list, please verify it and try again.",
	["wrn_typ_mis"]	=		"File-type not set, or invalid. Please try again with valid input.",
	["wrn_typ_ext"]	=		"File-type already exists, please try again with a unique name.",
	["wrn_fil_rng"]	=		"Min/max range not set, or invalid. Please try again with valid input.",
	["wrn_fil_nam"]	=		"Title or filename missing, please provide both as they are required.",
	["wrn_usr_nck"]	=		"No nick passed, or not valid. Please try again, without any of these characters: ",

	-- Status messages:
	["running"]		=		"ListBot is up and running, and ready for commands",
	["success"]		=		"Success! ",
	["t10_suc_1"]	=		"New file of id #",
	["t10_suc_2"]	=		"added to your Top-10 list, with a rank of",
	["t10_upd_1"]	=		"Updated file with id #",
	["t10_upd_2"]	=		"in your Top-10 list, changed rank to",
	["t10_del_1"]	=		"Removed file with id #",
	["t10_del_2"]	=		"from your Top-10 list.",
	["no_types"]	=		"No file-types available, please try again later.",
	["typ_del"]		=		"File-type deleted successfully.",
	["typ_saved"]	=		"New type saved successfuly.",
	["typ_cng_1"]	=		"Successfully changed type of file ",
	["typ_cng_2"]	=		"to type: ",
	["no_files"]	=		"Sorry, no files in database. Please try again later.",
	["no_range"]	=		"Sorry, no files found in that range. Please try again later.",
	["no_type"]		=		"Sorry, no files found of that type. Please try again later.",
	["no_fuser"]	=		"Sorry, no files found by that user. Please try again later.",
	["file_del"]	=		"File deleted successfully.",
	["file_added"]	=		"Added file successfully.",

	-- Operator-messages:
	["op_typ_del"]	=		"deleted type",
	["op_typ_add"]	=		"added type",
	["op_fil_del"]	=		"deleted file with id #",
	["op_fil_add"]	=		"added file",

	-- Listing messages:
	["t10_list"]	=		"Top-10 list for",
	["typ_list"]	=		"Showing types:",
	["new_list"]	=		"newest files",
	["range_list"]	=		"Showing files from ID",
	["user_list"]	=		"ListBot users",
	["user_files"]	=		"Files added by ",
	["rank"]		=		"Rank #",
	["type"]		=		"Type: ",
	["poster"]		=		"Poster: ",
	["fid"]			=		"File ID: ",
	["file"]		=		"File #",
	["added"]		=		"Added: ",
	["fname"]		=		"Filename: ",
	["path"]		=		"Path: ",
	["cmnt"]		=		"Comment: ",
	["url"]			=		"URL: ",

	-- Misc. messages:
	["user"]		=		"user",
	["showing"]		=		"showing",
	["files"]		=		"files",

	-- Help messages:
	["h_header"]	=		"Commands for use with this bot:",
	["h_help"]		=		"Shows this help-page.",
	["h_ltype"]		=		"Shows a list over all items of given <type_id>.",
	["h_ltypes"]	=		"Shows a list over all types, and their ID number.",
	["h_lnew"]		=		"Shows a list over the 10 newest files added.",
	["h_lall"]		=		"Shows a list over all files in the database.",
	["h_lrange"]	=		"Lists all files between ID \"S\" and \"E\", including.",
	["h_ltop"]		=		"Shows Top-10 list for user, your own if no nick is supplied.",
	["h_atop"]		=		"Add a new file to your Top-10 list. Rank 1=highest, 0=lowest.",
	["h_rtop"]		=		"Change the rank of a file in your Top-10 list.",
	["h_dtop"]		=		"Delete file from your Top-10 list.",
	["h_ntype"]		=		"Adds a new type with <type> as it's name.",
	["h_dtype"]		=		"*WARNING* Deletes the type with the given ID, and all the files associated with that ID.",
	["h_ctype"]		=		"Change type of file \"fid\" to type with id \"tid\".",
	["h_fadd"]		=		"\t\tAdds a new file of the type <type_id>. Type, title and filename are required,\r\n"..
							"\t\tand must be filled out correctly. The comment is a good place to add the file-size,\r\n"..
							"\t\tand other various notes about the file. Use quotes if you have space in the fields.\r\n",
	["h_fdel"]		=		"Deletes the file with the given ID, to add it again you have to use ",
	["h_lbs"]		=		"Toggles the given action between ON and OFF, valid options are:\r\n"..
							"\t\t'menu' for Right-Click menu, 'alert' for OP alert, 'news' for PM news to connecting users.\r\n"..
							"\t\t'webuse' to add URLs in lists, 'webpath' to set the domain, 'newno' to set the # of news in \"!list new\".",

	-- Right-click menu:
	["rm1_1"]		=		"Show last",
	["rm1_2"]		=		"files",
	["rm2"]			=		"List all files",
	["rm3"]			=		"Show a range of files",
	["rm3_p1"]		=		"Minimum file ID?",
	["rm3_p2"]		=		"Maximum file ID?",
	["rm4"]			=		"List files of type #",
	["rm4_p1"]		=		"Enter a type ID",
	["rm5"]			=		"Add a file",
	["rm5_p1"]		=		"Enter destination type ID",
	["rm5_p2"]		=		"The title (ex: Demo video)",
	["rm5_p3"]		=		"The filename (ex: demo vid.avi)",
	["rm5_p4"]		=		"Path to file (ex: /movies/new)",
	["rm5_p5"]		=		"Comment",
	["rm6"]			=		"Change type of file",
	["rm6_p1"]		=		"File ID",
	["rm6_p2"]		=		"New type ID",
	["rm7"]			=		"Delete a file",
	["rm7_p1"]		=		"ID of file to delete?",
	["rm8"]			=		"List all types",
	["rm9"]			=		"Add a new type",
	["rm9_p1"]		=		"Enter a new type name",
	["rm10"]		=		"Delete a type",
	["rm10_p1"]		=		"**WARNING** Deletes all associated files!",
	["rm11"]		=		"Display top-10 list",
	["rm11_p1"]		=		"User's nick, empty for your own list",
	["rm12"]		=		"Add new top-10 file",
	["rm12_p1"]		=		"Enter file ID",
	["rm12_p2"]		=		"Enter rank (1 - (1)0)",
	["rm13"]		=		"Rank a top-10 file",
	["rm13_p1"]		=		"Enter new rank (1 - (1)0)",
	["rm14"]		=		"Remove a top-10 file",
	["rm15"]		=		"Help for ListBot",
	["rm16"]		=		"Toggle OP alert ON/OFF",
	["rm17"]		=		"Toggle News on connection",
	["rm18"]		=		"Set number of news",
	["rm18_p1"]		=		"Number of news to show in list",
	["rm19"]		=		"Toggle send this menu",
	["rm20"]		=		"Toggle Top-10 lists",
	["rm21"]		=		"Set Top-10 class",
	["rm21_p1"]		=		"Class of users allowed to have Top-10 lists",
	["rm22"]		=		"Toggle use URL",
	["rm23"]		=		"Set base URL",
	["rm23_p1"]		=		"Base path for URLs",
	["rm24"]		=		"Set language",
	["rm24_p1"]		=		"Language code (ex: en)",
	["rm25"]		=		'Sett user class for "add"',
	["rm25_p1"]		=		"Class of users allowed to add files and types",

	-- LBS messages:
	["lbs_op_on"]	=		"OP alert is now [ON]!",
	["lbs_op_off"]	=		"OP alert is now [OFF]!",
	["lbs_new_on"]	=		"PM news to connecting users is now [ON]!",
	["lbs_new_off"]	=		"PM news to connecting users is now [OFF]!",
	["lbs_new_set"]	=		"Number of news set to: ",
	["lbs_mnu_on"]	=		"Right-click menu is now [ON]!",
	["lbs_mnu_off"]	=		"Right-click menu is now [OFF]!",
	["lbs_t10_on"]	=		"Top-10 lists are now [ON]!",
	["lbs_t10_off"]	=		"Top-10 lists are now [OFF]!",
	["lbs_t10_set"]	=		"Top-10 user class set to:",
	["lbs_web_on"]	=		"Web-path use is now [ON]!",
	["lbs_web_off"]	=		"Web-path use is now [OFF]!",
	["lbs_web_set"]	=		"Web-path is set to: ",
	["lbs_lan_set"]	=		"Language changed to: ",
	["lbs_lan_err"]	=		"Could not change language. Make sure language code is 2 chars only, and the language file exists.",
	["lbs_add_set"]	=		"Minimum class for add-commands successfully set to ",
	["lbs_val_err"]	=		"No valid value set, please try again",
	["lbs_err"]		=		"Unkown or missing rule filter, please try again"
};